package org.inb.biomoby.service;

import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import org.inb.biomoby.shared.message.MobyDataElement;
import org.inb.biomoby.shared.message.MobyMessage;
import org.inb.biomoby.shared.wsrf.MobyEndpointReference;
import org.inb.biomoby.shared.wsrf.PropertyResponseWrapper;
import org.inb.biomoby.shared.wsrf.SubmitResponseWrapper;
import org.inb.biomoby.shared.wsrf.fault.InvalidQueryExpressionFault;
import org.inb.biomoby.shared.wsrf.fault.InvalidResourcePropertyQNameFault;
import org.inb.biomoby.shared.wsrf.fault.QueryEvaluationErrorFault;
import org.inb.biomoby.shared.wsrf.fault.ResourceNotDestroyedFault;
import org.inb.biomoby.shared.wsrf.fault.ResourceUnavailableFault;
import org.inb.biomoby.shared.wsrf.fault.ResourceUnknownFault;
import org.inb.biomoby.shared.wsrf.fault.UnknownQueryExpressionDialectFault;
import org.inb.lsae.AnalysisEvent;
import org.inb.wsrf.rp2.GetMultipleResourceProperties;
import org.inb.wsrf.rp2.GetMultipleResourcePropertiesResponse;
import org.inb.wsrf.rp2.GetResourcePropertyResponse;
import org.inb.wsrf.rp2.InvalidResourcePropertyQNameFaultType;
import org.inb.wsrf.rp2.QueryExpressionType;
import org.inb.wsrf.rp2.QueryResourceProperties;
import org.inb.wsrf.rp2.QueryResourcePropertiesResponse;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @author Dmitry Repchevsky
 */

public abstract class AbstractAsyncMobyService implements IAsyncMobyService
{
    private String invocationId;
    
    public String getInvocationId()
    {
        return invocationId;
    }
    
    public void setInvocationId(String invocationId)
    {
        this.invocationId = invocationId;
    }
    
    public abstract IAsyncMobyImpl getAsyncMobyImpl();
    public abstract WebServiceContext getWebServiceContext();

    /**
     * Proxy method to obtain a serviceInvocationId
     * @return customized serviceInvocationId or null
     */
    public String getServiceInvocationId()
    {
        return getAsyncMobyImpl().getServiceInvocationId();
    }

    public MobyMessage call(MobyMessage message)
    {
//        HeaderList hl = (HeaderList)getWebServiceContext().getMessageContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
//        for (Iterator<Header> iter = hl.iterator(); iter.hasNext();)
//        {
//            
//        }
//                
//        if (expressions != null)
//        {
//            try
//            {
//                MobyEndpointReference ref = new MobyEndpointReference(expressions);
//                MobyDispatch.MobyWSRFResource wsrf = new MobyDispatch.MobyWSRFResource(ref);
//
//                List<MobyDataElement> elements = wsrf.queryResourcePropertyResult(ref.expression);
//
//                for (MobyDataElement element : elements)
//                {
//                    
//                }
//            }
//            catch(Exception ex)
//            {
//                ex.printStackTrace();
//            }
//        }
        
        return getAsyncMobyImpl().call(message);
    }

    public SubmitResponseWrapper submit(MobyMessage message)
    {
        getAsyncMobyImpl().submit(message);

        SubmitResponseWrapper response;
        try
        {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();            
            
            Element parameter = doc.createElementNS(MOBY_NAMESPACE, MobyEndpointReference.REF_PARAM_NAME);
            parameter.setTextContent(getInvocationId());

            W3CEndpointReference ref = (W3CEndpointReference)getWebServiceContext().getEndpointReference(parameter);
//            W3CEndpointReference ref = (W3CEndpointReference) ctx.getEndpointReference();
            
            // we have to modify the reference according v2.4.2

//            MobyEndpointReference mobyRef = new MobyEndpointReference(ref);
//            
//            W3CEndpointReferenceBuilder wsaBuilder = new W3CEndpointReferenceBuilder();
//            wsaBuilder.address(mobyRef.url);
//            wsaBuilder.referenceParameter(parameter);
//            
//            ref = wsaBuilder.build();
            
            response = new SubmitResponseWrapper(ref);
        }
        catch(ParserConfigurationException ex)
        {
            response = null;
        }
        
        return response;
    }
    
    public GetResourcePropertyResponse getResourceProperty(QName getResourcePropertyRequest)
        throws InvalidResourcePropertyQNameFault, ResourceUnavailableFault, ResourceUnknownFault
    {
        return getResourcePropertyResponse(getResourcePropertyRequest);
    }

    public GetMultipleResourcePropertiesResponse getMultipleResourceProperties(GetMultipleResourceProperties getMultipleResourcePropertiesRequest)
        throws InvalidResourcePropertyQNameFault, ResourceUnavailableFault, ResourceUnknownFault
    {
        List<QName> qNames = getMultipleResourcePropertiesRequest.getResourceProperties();
        
        GetMultipleResourcePropertiesResponse response = new GetMultipleResourcePropertiesResponse();
        
        for (QName qName : qNames)
        {
            GetResourcePropertyResponse propertyResponse = getResourcePropertyResponse(qName);
            response.addResponse(propertyResponse);
        }
        
        return response;
    }

    public QueryResourcePropertiesResponse queryResourceProperties( QueryResourceProperties queryResourceProperties)
        throws UnknownQueryExpressionDialectFault, InvalidQueryExpressionFault, QueryEvaluationErrorFault
    {
        QueryResourcePropertiesResponse queryResourcePropertiesResponse = new QueryResourcePropertiesResponse();
        
        QueryExpressionType eType = queryResourceProperties.getQueryExpression();
        List expressions = eType.getContent();
        
        for (Object expression : expressions)
        {
            List<MobyDataElement> elements = getAsyncMobyImpl().queryResourceProperties(expression.toString());
            
            for (MobyDataElement element : elements)
            {
                queryResourcePropertiesResponse.addContent(element);
            }
        }
        
        return queryResourcePropertiesResponse;
    }
    
    public void destroy()
        throws ResourceNotDestroyedFault, ResourceUnavailableFault, ResourceUnknownFault
    {
        getAsyncMobyImpl().destroy();
    }


    private GetResourcePropertyResponse getResourcePropertyResponse(QName qName) throws InvalidResourcePropertyQNameFault, ResourceUnavailableFault, ResourceUnknownFault
    {
        String propertyId = qName.getLocalPart();
        
        PropertyResponseWrapper wrapper;
        if (propertyId.startsWith("status_"))
        {
            String queryID = propertyId.substring("status_".length());
            
            AnalysisEvent lsae = getAsyncMobyImpl().getResourcePropertyStatus(queryID);
            wrapper = new PropertyResponseWrapper(lsae);
        }
        else if (propertyId.startsWith("result_"))
        {
            String queryID = propertyId.substring("result_".length());
            
            MobyMessage message = getAsyncMobyImpl().getResourcePropertyResult(queryID);
            wrapper = new PropertyResponseWrapper(message);
        }
        else
        {
            throw new InvalidResourcePropertyQNameFault("Unknown property type: " + propertyId, new InvalidResourcePropertyQNameFaultType());
        }

        // polymorthic wrapper
        JAXBElement<AnalysisEvent> element = new JAXBElement(new QName(MOBY_NAMESPACE, propertyId, qName.getPrefix()), PropertyResponseWrapper.class, wrapper);

        return new GetResourcePropertyResponse(element);
    }
}
