/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.axis2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.jws.HandlerChain;
import javax.naming.Reference;
import javax.servlet.ServletContext;

import org.ow2.easybeans.server.Embedded;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.ws.axis2.easybeans.Axis2EJBWebserviceEndpoint;
import org.ow2.jonas.ws.axis2.jaxws.Axis2WSEndpoint;
import org.ow2.jonas.ws.axis2.jaxws.WebservicesContainer;
import org.ow2.jonas.ws.jaxws.IJAXWSService;
import org.ow2.jonas.ws.jaxws.IWebServiceEndpoint;
import org.ow2.jonas.ws.jaxws.IWebservicesModule;
import org.ow2.jonas.ws.jaxws.PortMetaData;
import org.ow2.jonas.ws.jaxws.WSException;
import org.ow2.jonas.ws.jaxws.base.JAXWSService;
import org.ow2.jonas.ws.jaxws.base.JAXWSWebservicesModule;
import org.ow2.jonas.ws.jaxws.easybeans.naming.WebServiceRefExtensionListener;
import org.ow2.util.ee.metadata.common.api.struct.IJaxwsWebServiceRef;
import org.ow2.util.ee.metadata.common.api.xml.struct.IHandler;
import org.ow2.util.ee.metadata.common.api.xml.struct.IHandlerChain;
import org.ow2.util.ee.metadata.war.api.IWarClassMetadata;
import org.ow2.util.ee.metadata.ws.api.struct.IWebServiceMarker;
import org.ow2.util.event.api.IEventService;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
/**
 * The Axis2Service class is used to declare Axis2 as a module in JOnAS.
 *
 * @author xiaoda
 */
public class Axis2Service extends JAXWSService implements IJAXWSService {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(Axis2Service.class);

    /**
     * Each WebApp may be associated with one WSModule.
     */
    Map<String, IWebservicesModule<WebservicesContainer<Axis2EJBWebserviceEndpoint>>> webservicesModules = new HashMap<String,  IWebservicesModule<WebservicesContainer<Axis2EJBWebserviceEndpoint>>>();

    /**
     * The event service used to register listeners.
     */
    private IEventService eventService;

    /**
     * The naming extension listener for EasyBean's java:comp/env building.
     */
    private WebServiceRefExtensionListener listener;


    @Override
    protected void doStart() throws ServiceException {
        super.doStart();
    }

    @Override
    protected void doStop() throws ServiceException {
        super.doStop();
    }

    public Reference createNamingReference(final IJaxwsWebServiceRef serviceRef) {
        return null;

    }

    public IWebServiceEndpoint createPOJOWebServiceEndpoint(final IWarClassMetadata metadata,
                                                            final ClassLoader loader,
                                                            final ServletContext servletContext) throws WSException {
        // Find a unique name for the WebModule
        String name = findUniqueContextName(servletContext);

        // The module linked to this archive
        IWebservicesModule<WebservicesContainer<Axis2EJBWebserviceEndpoint>> module = webservicesModules.get(name);
        if (module == null) {
            module = new  JAXWSWebservicesModule<WebservicesContainer<Axis2EJBWebserviceEndpoint>>(name);
            webservicesModules.put(name, module);
        }

        // TODO Auto-generated method stub
        String classname = metadata.getJClass().getName().replace('/', '.');

        Class<?> klass = null;
        try {
            klass = loader.loadClass(classname);
        } catch (ClassNotFoundException e) {
            throw new WSException("Unable to load the WebService class: " + classname, e);
        }

        IWebServiceMarker wsMarker = metadata.getWebServiceMarker();

        JOnASJaxWsImplementorInfo jaxWsImplementorInfo = new JOnASJaxWsImplementorInfo(klass, wsMarker);

        // Get a Container for all services deployed using the same WSDL
        WebservicesContainer container = module.findContainer(jaxWsImplementorInfo.getWsdlLocation());
        if (container == null) {
            // Create a new Container
            container = new WebservicesContainer(jaxWsImplementorInfo.getWsdlLocation());
            module.addContainer(container);

        }

        // FIXME
        // Using annotation only values (simple but not sufficent)
        String wsdlLocation = jaxWsImplementorInfo.getWsdlLocation();

        if (wsdlLocation == null || "".equals(wsdlLocation)) {
            logger.debug("The WSDL location is not specified!");
        }

        // find the URL pattern to use

        // FIXME
        // The util class only look for annotation on the given class
        // JAXWS defines precises rules when inheritance comes into play
        // They're not respected in this case
        // BTW, IWSMarker contains XML descriptor values overriding annotation values
        String serviceName = jaxWsImplementorInfo.getServiceName().getLocalPart();

        String pattern = null;
        if (!"".equals(serviceName)) {
            // default pattern : /{service-name}
            pattern = "/" + serviceName;
        } else {
            // else /{class.name}Service
            pattern = "/" + klass.getSimpleName() + "Service";
        }

        // Creates PortMetaData to hold deployment/runtime information
        // about the web service
        PortMetaData pmd = new PortMetaData();

        pmd.setUrlPattern(pattern);
        pmd.setWSDLLocation(wsdlLocation);

        StringBuffer handlerXML = new StringBuffer();




        // if we have define the handlerchain in webservices.xml, should override annotation
        if(wsMarker.getHandlerChains()!=null) {
            handlerXML.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><jws:handler-chains xmlns:jws=\"http://java.sun.com/xml/ns/javaee\">");
            for(IHandlerChain hc : wsMarker.getHandlerChains().getHandlerChains()) {
                handlerXML.append("<jws:handler-chain>");
                for(IHandler ih : hc.getHandlers()) {
                    handlerXML.append("<jws:handler>");
                    handlerXML.append("<jws:handler-name>" + ih.getHandlerName() + "</jws:handler-name>");
                    handlerXML.append("<jws:handler-class>" + ih.getHandlerClass() + "</jws:handler-class>");
                    handlerXML.append("</jws:handler>");
                }
                handlerXML.append("</jws:handler-chain>");
            }
            handlerXML.append("</jws:handler-chains>");
            pmd.setHandlerXML(handlerXML.toString());
        } else {
            try {
                HandlerChain hc = klass.getAnnotation(HandlerChain.class);

                if (hc != null) {
                    URL handlerURL = klass.getResource(hc.file());
                    BufferedReader br = new BufferedReader(new InputStreamReader(handlerURL.openStream()));

                    String temp = null;
                    temp = br.readLine();
                    while (temp != null) {
                        handlerXML.append(temp);
                        temp = br.readLine();
                    }
                    pmd.setHandlerXML(handlerXML.toString());
                }

            } catch (IOException e) {
                throw new WSException("Unable to Get the handler description file for" + serviceName + "!", e);
            } finally {
                if(pmd.getHandlerXML() != null) {
                    logger.debug("Found handlers in this package!");
                }
            }
        }


        Axis2WSEndpoint endpoint = null;
        try {
            endpoint = new Axis2WSEndpoint(klass, IWebServiceEndpoint.EndpointType.POJO, pmd, null,new URL("file",null,name));
        } catch (MalformedURLException e1) {
            throw new WSException("Invalid context root path: " + "file:/ " + name,e1);
        }

        try {
            // init axis2
            endpoint.init();
        } catch (Exception e) {
            throw new WSException("Exception occurs when creating POJO endpoint for" + serviceName + "!",e);
        }

        logger.info("Endpoint {0} inited", serviceName);
        // Link the endpoint
        container.addEndpoint(endpoint);

        return endpoint;
    }

    /**
     * Finalize the deployment of POJO endpoints contained in the given ServletContext.
     *
     * @param context ServletContext.
     */
    public void finalizePOJODeployment(final ServletContext context) {
        // TODO To be implemented
        //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * Stop and undeploy the POJO endpoints contained in the given ServletContext.
     *
     * @param context ServletContext.
     */
    public void undeployPOJOEndpoints(final ServletContext context) {
        // TODO To be implemented
        //To change body of implemented methods use File | Settings | File Templates.
        String name = findUniqueContextName(context);

        IWebservicesModule<WebservicesContainer<Axis2EJBWebserviceEndpoint>> module = webservicesModules.get(name);
        if (module != null) {
            module.stop();
            // Remove the module from the list
            webservicesModules.remove(name);
        }
    }


    public void bindEventService(final IEventService eventService) {
        logger.info("EventService instance {0}", eventService);
        this.eventService = eventService;
        listener = new WebServiceRefExtensionListener();
        eventService.registerListener(listener, Embedded.NAMING_EXTENSION_POINT);
    }

    public void unbindEventService(final IEventService eventService) {
        this.eventService.unregisterListener(listener);
        this.eventService = null;
    }
    private String findUniqueContextName(final ServletContext context) {
        return context.getRealPath("/");
    }

}
