/*
 *    GATEServices.java
 *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.greenstone.gsdl3.service;


// Greenstone classes
import org.greenstone.gsdl3.util.*;

// GATE classes
import gate.*;
import gate.creole.*;
import gate.gui.*;
import gate.util.persistence.PersistenceManager;

// XML classes
import org.w3c.dom.Element; 
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

// General Java classes
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


/**
 * <p>Title: GATE Greenstone3 integration</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: University of Waikato</p>
 * @author unascribed
 * @version 1.0
 */

public class GATEServices
    extends ServiceRack {

    // the services on offer
    // these strings must match what is found in the properties file
    protected static final String GATE_TAG_SERVICE = "GateTag";

    protected static final String ANNOTATION_TYPE_PARAM = "annotationType";

    protected Element config_info = null;

    protected CorpusController application;

    protected Corpus corpus;

    protected String[] annotation_types = { "Date", "Location",
					     "Organization", "Person" };
    // Address, 

    /** constructor */
    public GATEServices()
    {
    }


    /** configure this service */
    public boolean configure(Element info, Element extra_info)
    {
	System.out.println("Configuring GATEServices...");
	this.config_info = info;

	// set up short_service_info_ - for now just has name and type
	Element tag_service = this.doc.createElement(GSXML.SERVICE_ELEM);
	tag_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_ENRICH);
	tag_service.setAttribute(GSXML.NAME_ATT, GATE_TAG_SERVICE);
	this.short_service_info.appendChild(tag_service);


	// Configure GATE for use
	try {
	    Gate.init();
	    Gate.getUserConfig().put(GateConstants.DOCUMENT_ADD_SPACE_ON_UNPACK_FEATURE_NAME,
				     new Boolean(false));

	    // MainFrame mainFrame = new MainFrame();
	    // mainFrame.setSize(new java.awt.Dimension(800, 600));
	    // mainFrame.setVisible(true);

	    // Load the (pre-created) application
	    URL applicationFileURL = ClassLoader.getSystemResource("gate.app");
	    File applicationFile = new File(applicationFileURL.getFile());
	    this.application = (CorpusController) PersistenceManager.loadObjectFromFile(applicationFile);

	    /* Collection processing_resources = application.getPRs();
	    Iterator pr_iterator = processing_resources.iterator();
	    while (pr_iterator.hasNext()) {
		ProcessingResource pr = (ProcessingResource) pr_iterator.next();
		String pr_name = pr.getName();
		System.out.println("PR name: " + pr_name);
		if (pr_name.startsWith("ANNIE POS Tagger")) {
		    String as_name = ((POSTagger) pr).getInputASName();
		    System.out.println("AS name: " + as_name);
		}
		} */

	    // Create a new corpus
	    this.corpus = Factory.newCorpus("GSDL3 Corpus");
	    this.application.setCorpus(this.corpus);
	}
	catch (Exception e) {
	    e.printStackTrace();
	}

	return true;
    }

    protected Element getServiceDescription(String service, String lang, String subset) {

	if (!service.equals(GATE_TAG_SERVICE)) {
	    return null;
	}
	Element tag_service = this.doc.createElement(GSXML.SERVICE_ELEM);
	tag_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_ENRICH);
	tag_service.setAttribute(GSXML.NAME_ATT, GATE_TAG_SERVICE);
	if (subset==null || subset.equals(GSXML.DISPLAY_TEXT_ELEM)) {
	    tag_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service+".name", lang)));
	    tag_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service+".description", lang)));
	    tag_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service+".submit", lang)));
	} 
	if (subset==null || subset.equals(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER)) {
	    Element param_list = this.doc.createElement(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
	    createParameter(ANNOTATION_TYPE_PARAM, param_list, lang);
	    tag_service.appendChild(param_list);
	}
	return tag_service;
    }


    /** creates a new param element and adds it to the param list */
    protected void createParameter(String name, Element param_list, 
				   String lang)
    {
	Element param = null;

	if (name.equals(ANNOTATION_TYPE_PARAM)) {
	    int len = this.annotation_types.length;
	    String[] annotation_type_names = new String[len];
	    for (int i = 0; i < len; i++) {
		annotation_type_names[i] = getTextString("param." + name + "." + this.annotation_types[i], lang);
	    }
	    
	    param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_ENUM_MULTI, this.annotation_types[0], this.annotation_types, annotation_type_names);
	    param_list.appendChild(param);
	}
	
    }
    
    
    protected Element processGateTag(Element request)
    {
	// System.out.println("(GateTag) Request:\n" + converter_.getPrettyString(request));

	// Create a new (empty) result message
	Element result = this.doc.createElement(GSXML.RESPONSE_ELEM);
	result.setAttribute(GSXML.FROM_ATT, GATE_TAG_SERVICE);
	result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);

	// Get the parameters of the request
	Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
	if (param_list == null) {
	    System.err.println("Error: GateTag request had no paramList.");
	    return result;  // Return the empty result
	}

	// Process the request parameters
	Set annTypes = new HashSet();
	Element param = (Element) param_list.getFirstChild();
	while (param != null) {
	    // Identify the annotation types desired
	    if (param.getAttribute(GSXML.NAME_ATT).equals("annotationType")) {
		String annotation_type = GSXML.getValue(param);
		String [] types = annotation_type.split(",");
		for (int i=0; i<types.length; i++) {
		    annTypes.add(types[i]);
		}
	    }

	    param = (Element) param.getNextSibling();
	}

	// Get the request content 
      	Element doc_node_list = (Element) GSXML.getChildByTagName(request, GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
	if (doc_node_list == null) {
	    System.err.println("Error: GateTag request specified no doc nodes.");
	    return result;  // Return the empty result
	}

	// Process each document node in the list
	NodeList doc_nodes = doc_node_list.getChildNodes();
	for (int i = 0; i < doc_nodes.getLength(); i++) {
	    Element doc_node = (Element) doc_nodes.item(i);
	    Element content = (Element) GSXML.getChildByTagName(doc_node, "nodeContent");
	    Node content_text = (Node) GSXML.getNodeTextNode(content);
	    String text = content_text.getNodeValue();

	    // GATE needs the text to be a valid HTML file
	    text = "<html><head></head><body>" + text + "</body></html>";
	    String annotated_text = processText(text, annTypes);

	    // Remove the surrounding HTML tags
	    annotated_text = annotated_text.substring(49, annotated_text.length() - 13);
	    // System.out.println("GATE result:\n" + annotated_text);

	    annotated_text = "<nodeContent>" + annotated_text + "</nodeContent>";
	    Document annotated_content_doc = this.converter.getDOM(annotated_text);
	    if (annotated_content_doc != null) {
		Element annotated_content = annotated_content_doc.getDocumentElement();
		doc_node.replaceChild(doc_node.getOwnerDocument().importNode(annotated_content, true), content);
	    } else {
		System.err.println("GATEServices.processGateTag Error: Couldn't parse annotated text for doc node "+i);
	    }
	    
	}

	result.appendChild(this.doc.importNode(doc_node_list, true));
	// System.out.println("GateTag result:\n" + converter_.getPrettyString(result));
	return result;
    }


    public String processText(String text, Set annotationTypesToExport)
    {
	try {
	    // Create a new document containing the text
	    FeatureMap parameters = Factory.newFeatureMap();
	    parameters.put("stringContent", text);
	    parameters.put("markupAware", new Boolean(true));
	    parameters.put("preserveOriginalContent", new Boolean(true));
	    parameters.put("collectRepositioningInfo", new Boolean(true));
	    Document doc = (Document) Factory.createResource("gate.corpora.DocumentImpl",
							     parameters);

	    // Add it to the corpus
	    this.corpus.clear();
	    this.corpus.add(doc);

	    // Process the corpus
	    this.application.execute();

	    // Extract all the annotations
	    AnnotationSet annSet = doc.getAnnotations();

	    // Return the desired annotations
	    AnnotationSet outputAnnotations = annSet.get(annotationTypesToExport);
	    String result = doc.toXml(outputAnnotations, false);
	    Factory.deleteResource(doc);

	    // 1. Escape the GATE result
	    result = GSXML.xmlSafe(result);

	    // 2. Unescape the annotation tags
	    Iterator setIterator = annotationTypesToExport.iterator();
	    while (setIterator.hasNext()) {
		String annotationType = (String) setIterator.next();
		result = result.replaceAll("&lt;" + annotationType + "&gt;",
					   "<annotation type=\"" + annotationType + "\">");
		result = result.replaceAll("&lt;/" + annotationType + "&gt;",
					   "</annotation>");
	    }

	    return result;
	}
	catch (Exception e) {
	    e.printStackTrace();
	    return null;
	}
    }
}
