package org.inb.biomoby.service;

import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.inb.biomoby.shared.message.MobyData;
import org.inb.biomoby.shared.message.MobyDataElement;
import org.inb.biomoby.shared.message.MobyMessage;
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.ResourceUnavailableFault;
import org.inb.biomoby.shared.wsrf.fault.ResourceUnknownFault;
import org.inb.biomoby.shared.wsrf.fault.UnknownQueryExpressionDialectFault;
import org.inb.wsrf.r2.ResourceUnknownFaultType;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * @author Dmitry Repchevsky
 */

public abstract class AbstractAsyncMobyImpl implements IAsyncMobyImpl
{
    private static JAXBContext jbctx;
    
    private synchronized JAXBContext getContext() throws JAXBException
    {
        if (jbctx == null)
        {
            // NO ONTOLOGY!!! 
            // WE CAN NOT INJECT ONTOLOGY CLASSES FOR THE WEBSERVICE REPLY
            // WITHOUT PROVIDING THEM TO THE "WebServiceContext"
            return jbctx = JAXBContext.newInstance("org.inb.biomoby.shared.messages:org.inb.biomoby.shared.datatypes:org.inb.lsae:org.inb.wsrf.bf2:org.inb.wsrf.r2:org.inb.wsrf.rl2:org.inb.wsrf.rp2:org.inb.biomoby.shared.wsrf");
        }
        
        return jbctx;
    }
    
    public MobyMessage getResourcePropertyResult(String queryID) throws InvalidResourcePropertyQNameFault, ResourceUnavailableFault, ResourceUnknownFault
    {
        MobyMessage message = getResourcePropertyResult();
        
        ArrayList<MobyData> list = message.getMobyDataList();
        
        for (MobyData mobyData : list)
        {
            if (queryID.equals(mobyData.getQueryID()))
            {
                return new MobyMessage(mobyData);
            }
        }
                
        throw new ResourceUnknownFault("unknown queryID (" + queryID + ")", new ResourceUnknownFaultType());
    }

    public List<MobyDataElement> queryResourceProperties(String expression) throws UnknownQueryExpressionDialectFault, InvalidQueryExpressionFault, QueryEvaluationErrorFault
    {
        List<MobyDataElement> list = new ArrayList<MobyDataElement>();
        
        try
        {
            MobyMessage message = getResourcePropertyResult();
            
            JAXBContext context = getContext();
            JAXBSource jaxbSource = new JAXBSource(context, message);

            // TODO look how to stream jaxbSource!!!
            TransformerFactory f = TransformerFactory.newInstance();
            Transformer tr = f.newTransformer();

            CharArrayWriter writer = new CharArrayWriter();
            StreamResult result = new StreamResult(writer);
            tr.transform(jaxbSource, result);

            InputSource saxSource = new InputSource(new CharArrayReader(writer.toCharArray()));
            
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            
            NodeList nodes = (NodeList) xpath.evaluate(expression, saxSource, XPathConstants.NODESET);
           
            Unmarshaller u = context.createUnmarshaller();
            
            for (int i = 0; i < nodes.getLength(); i++)
            {
                Node node = nodes.item(i);                
                MobyDataElement data = (MobyDataElement) u.unmarshal(node);
                list.add(data);
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        
        return list;
    }
}
