/*
 * Created on Nov 17, 2004
 * Copyright (C) Andrea Schweer, 2004
 *
 * This file is part of the Greenstone Alerting Service.
 * Refer to the COPYING file in the base directory of this package
 * for licensing information.
 */
package org.greenstone.gsdlas;

import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.*;

import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.rpc.*;
import org.w3c.dom.*;

import org.greenstone.gsdlas.util.ArrayHelper;

/**
 * @author schweer
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class GreenstoneCommunicator {

    private URL host;
    private DocumentBuilder builder;

    public GreenstoneCommunicator(URL host) throws Exception {
        this.host = host;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            builder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new Exception(e);
        }

    }

    /**
     * 
     */
    public GreenstoneCommunicator() {
        
        // TODO Auto-generated constructor stub
    }

    public String[] getHostNames() {
        // TODO method implementation
        return new String[] {"localhost"};
    }
    
    public String[] getCollectionNames() throws Exception {
        Document document = createSubsetDescriptionRequest("", "collectionList");

        Element response = sendToGreenstone(document.getDocumentElement());
        
        NodeList nodeList = response.getElementsByTagName("collection");
        String[] result = new String[nodeList.getLength()];
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            Node name = node.getAttributes().getNamedItem("name");
            result[i] = name.getNodeValue();
        }
        
	System.out.println(ArrayHelper.toString(result));
        
        return result;
    }
    
    public boolean supportsTextSearch(String collection) throws Exception {
        Document document = createSubsetDescriptionRequest(collection, "serviceList");
        
        Element response = sendToGreenstone(document.getDocumentElement());
        
        NodeList nodeList = response.getElementsByTagName("service");
        
        boolean hasTextQuery = false;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            String name = node.getAttributes().getNamedItem("name").getNodeValue(); 
            if (name.equals("TextQuery")) {
                hasTextQuery = true;
                break;
            }
        }
        
        if (!hasTextQuery) return false; // doesn't support text queries at all
        
        document = createSubsetDescriptionRequest(collection + "/TextQuery", "paramList");
        
        response = sendToGreenstone(document.getDocumentElement());
        
        nodeList = response.getElementsByTagName("param");
        
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            String name = node.getAttributes().getNamedItem("name").getNodeValue();
            if (name == null) break;
            String value = node.getAttributes().getNamedItem("type").getNodeValue();
            if (value == null) break;
            if (name.equals("query") && value.equals("string")) {
                return true;
            }
        }
        
        return false;
    }
    
    public Set fullTextSearch(String collection, String query) throws Exception {
        Document document = builder.newDocument();
        Element root = document.createElement("message");
        document.appendChild(root);
    
        Element requestElement = document.createElement("request");
        requestElement.setAttribute("lang", "en");
        requestElement.setAttribute("type", "process");
        requestElement.setAttribute("to", collection + "/TextQuery");
        root.appendChild(requestElement);
    
        Element paramListElement = document.createElement("paramList");
        requestElement.appendChild(paramListElement);
    
        Element paramElement = document.createElement("param");
        paramElement.setAttribute("name", "query");
        paramElement.setAttribute("value", query);
        // BTS 3 what if numReturnedDocs < numMatchedDocs
        paramListElement.appendChild(paramElement);
        
        
        Set result = new TreeSet();
        //DOM2Writer.serializeAsXML(root, new OutputStreamWriter(System.out));
        
        Element response = sendToGreenstone(document.getDocumentElement());
        
        NodeList documentNodes = response.getElementsByTagName("documentNode");
        
        for(int i = 0; i < documentNodes.getLength(); i++) {
            Node node = documentNodes.item(i);
            String docID = node.getAttributes().getNamedItem("nodeID").getNodeValue();
            Matcher matcher = Pattern.compile("(\\d{8}:\\d+).*").matcher(docID);
            if (matcher.matches()) {
                docID = matcher.group(1);
            }
            result.add(docID);
        }
        
        return result;
    }
    
    private Element sendToGreenstone(Element message) throws Exception {
        Call call = new Call();
        // TODO get the uri from a configuration file
        call.setTargetObjectURI("localsite");
        call.setMethodName("process");
        // set Encoding Style to use literal XML
        call.setEncodingStyleURI(Constants.NS_URI_LITERAL_XML);
        // Set Method Parameters - use literal xml
        Parameter param = new Parameter("message", 
                org.w3c.dom.Element.class,
                message, 
                Constants.NS_URI_LITERAL_XML);

        Vector paramList = new Vector();
        paramList.addElement(param);
        call.setParams(paramList);

        // Invoke the Service
        Response response = call.invoke(host, "");

        // Check for Faults
        if (!response.generatedFault()) { // no error
            // Extract Return value
            Parameter result = response.getReturnValue();
            return (Element) result.getValue();
        } else {
            //  Extract Fault Code and String
            Fault f = response.getFault();
            String faultCode = f.getFaultCode();
            String faultString = f.getFaultString();
            throw new Exception(faultCode + ": Fault occured: " + faultString);
        }
    }

    /**
     * @return
     */
    private Document createSubsetDescriptionRequest(String object, String subsetType) {
        Document document = builder.newDocument();
        Element root = document.createElement("message");
        document.appendChild(root);
    
        Element requestElement = document.createElement("request");
        requestElement.setAttribute("lang", "en");
        requestElement.setAttribute("type", "describe");
        requestElement.setAttribute("to", object);
        root.appendChild(requestElement);
    
        Element paramListElement = document.createElement("paramList");
        requestElement.appendChild(paramListElement);
    
        Element paramElement = document.createElement("param");
        paramElement.setAttribute("name", "subset");
        paramElement.setAttribute("value", subsetType);
        paramListElement.appendChild(paramElement);
        return document;
    }
}
