/*
 *    PharosImageISServiceRack.java
 *    Copyright (C) 2009 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 imageis;


// Greenstone classes
import org.greenstone.gsdl3.service.ServiceRack;
import org.greenstone.gsdl3.util.GSXML;
import org.greenstone.gsdl3.util.GSPath;

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

// java classes
import java.util.ArrayList;
import java.util.HashMap;

// pharos
//import PharosImageIS;

import org.apache.log4j.*;

/** Pharos Image Similarity Search service
 *
 * @author Katherine Don
 */

public class PharosImageISServiceRack
    extends ServiceRack 
{

   static Logger logger = Logger.getLogger(imageis.PharosImageISServiceRack.class.getName());

    
    // the search service
    protected static final String PHAROS_QUERY_SERVICE = "PharosQuery";

    // compulsory params
    protected static final String IMAGE_ID_PARAM = "id";
  //protected static final String COLLECTION_PARAM = "collection";
  protected ImageIS pharos_engine = null;
 
    public PharosImageISServiceRack()
    {
      pharos_engine = new ImageIS();
    }

    /** sets up the short service info for PharosQuery. If other services 
     * will be provided, should be added in the subclass configure
     * also looks for search format info, and document format info
     */
    public boolean configure(Element info, Element extra_info)
    {
	if (!super.configure(info, extra_info)){
	    return false;
	}

	logger.info("Configuring PharosImageISServiceRack...");
	
	this.config_info = info;

	// set up short_service_info_ - for now just has id and type. the name (lang dependent) will be added in if the list is requested.
	Element pq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
	pq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
	pq_service.setAttribute(GSXML.NAME_ATT, PHAROS_QUERY_SERVICE);
	pq_service.setAttribute(GSXML.HIDDEN_ATT, "true");
	this.short_service_info.appendChild(pq_service);

	// add some format info to service map if there is any - look in extra info
	// first look in buildConfig
	Element format = (Element)GSXML.getChildByTagName(info, GSXML.FORMAT_ELEM);

	if (format==null) {
	    String path = GSPath.appendLink(GSXML.SEARCH_ELEM, GSXML.FORMAT_ELEM);

      //note by xiao: instead of retrieving the first 'format' element inside the 'search'
      // element, we are trying to find the real format element which has at least one
      // 'gsf:template' child element. (extra_info is collectionConfig.xml)
      //format = (Element) GSXML.getNodeByPath(extra_info, path);
      Element search_elem = (Element) GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
      NodeList format_elems = null;
      if (search_elem != null) {
        format_elems = search_elem.getElementsByTagName(GSXML.FORMAT_ELEM);
      }
      for(int i=0; i<format_elems.getLength(); i++) {
          format = (Element)format_elems.item(i);
          if (format.getElementsByTagName("gsf:template").getLength() != 0) {
            break;
          }
      }
	}//end of if(format==null)
	//
	if (format != null) {
	    this.format_info_map.put(PHAROS_QUERY_SERVICE, this.doc.importNode(format, true));
	}
	
	// is the following relevant here. and what is it anyway??
	// look for document display format - for documentType
	/*	String path = GSPath.appendLink(GSXML.DISPLAY_ELEM, GSXML.FORMAT_ELEM);
	Element display_format = (Element)GSXML.getNodeByPath(extra_info, path);
	if (display_format != null) {
	    // check for docType option.
	    Element doc_type_opt = GSXML.getNamedElement(display_format, "gsf:option", GSXML.NAME_ATT, "documentType");
	    if (doc_type_opt != null) {
		String value = doc_type_opt.getAttribute(GSXML.VALUE_ATT);
		if (!value.equals("")) {
		    this.default_document_type = value;
		}
	    }
	    }*/

	return true;
    }
    
    /** returns the description of the TextQuery service. If a subclass 
     * provides other services they need to provides their own descriptions */
    public Element getServiceDescription(String service, String lang, String subset) 
    {
	if (!service.equals(PHAROS_QUERY_SERVICE)) {
	    return null;
	}

	Element pq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
	pq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
	pq_service.setAttribute(GSXML.NAME_ATT, PHAROS_QUERY_SERVICE);
	pq_service.setAttribute(GSXML.HIDDEN_ATT, "true");
	if (subset==null || subset.equals(GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER)) {
	    pq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getServiceName(PHAROS_QUERY_SERVICE, lang) ));
	    pq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getServiceSubmit(PHAROS_QUERY_SERVICE, lang) ));
	    pq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getServiceDescription(PHAROS_QUERY_SERVICE, lang)));
	}
	if (subset==null || subset.equals(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER)) {
	    Element param_list = this.doc.createElement(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
	    Element param = GSXML.createParameterDescription(this.doc, IMAGE_ID_PARAM, getTextString("param."+IMAGE_ID_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
	    param_list.appendChild(param);

	    pq_service.appendChild(param_list);
	}
	return pq_service;
	
    }

    // perhaps these should be changed to search down the class hierarchy for 
    // values - do can just put the info in the resource bundle to use it
    /** returns the default name a service */
    public String getServiceName(String service_id, String lang) {
	return getTextString(service_id+".name", lang);
    }
    
    /** returns the default description for a service */
    public String getServiceDescription(String service_id, String lang) {
	return getTextString(service_id+".description", lang);
    }

    /** returns the default submit button text for a service */
    public String getServiceSubmit(String service_id, String lang) {
	return getTextString(service_id+".submit", lang);

    }
    /** create an element to go into the search results list. A node element
     * has the form
     * <docNode nodeId='xxx' nodeType='root' docType='simple' rank='0.23'/>
     */
    public Element createDocNode(String node_id, String rank) {
	Element node = this.doc.createElement(GSXML.DOC_NODE_ELEM);
	node.setAttribute(GSXML.NODE_ID_ATT, node_id);
	node.setAttribute(GSXML.NODE_RANK_ATT, rank);
	// default values for now
	node.setAttribute(GSXML.DOC_TYPE_ATT, "simple");
	node.setAttribute(GSXML.NODE_TYPE_ATT, "root");
	return node;
    }

  
    /** do the actual query 
     * must be implemented by subclass */
  public Element processPharosQuery(Element request) {
    
    // Create a new (empty) result message ('doc' is in ServiceRack.java)
    Element result = this.doc.createElement (GSXML.RESPONSE_ELEM);
    result.setAttribute (GSXML.FROM_ATT, PHAROS_QUERY_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) {
      logger.error ("PharosQuery request had no paramList.");
      return result;  // Return the empty result
    }
    
    // Process the request parameters
    HashMap params = GSXML.extractParams (param_list, false);
    
    // Make sure an id has been specified
    String query = (String) params.get (IMAGE_ID_PARAM);
    if (query == null || query.equals ("")) {
      return result;  // Return the empty result
    }
     
    String search_results = null;
    try {
      logger.error("querying on "+this.cluster_name+", "+query);
      search_results = pharos_engine.query(this.cluster_name, query);
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
    }
    if (search_results == null || search_results.equals("")) {
      logger.error("Empty results string");
      return result;
    }

    Element document_list = this.doc.createElement (GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
    
    try {
      Document result_xml = this.converter.getDOM(search_results);
      if (result_xml == null) {
	logger.error("Couldn't parse XML");
	return result;
      }
      
      Element result_element = result_xml.getDocumentElement();
      NodeList result_items = result_element.getElementsByTagName("ResultItem");
      String coll_prefix = this.cluster_name+":";
      int prefix_len = coll_prefix.length();
      for (int i=0; i<result_items.getLength(); i++) {
	Element item = (Element)result_items.item(i);
	String rank = item.getAttribute("confidence");
	Element resource = (Element)GSXML.getChildByTagName(item, "MediaResource");
	String id = GSXML.getNodeText(resource);
	
	System.err.println("id = "+id+", rank = "+rank);
	if (id.startsWith(coll_prefix)) {
	  id = id.substring(prefix_len);
	  id = id.replace(".jpg","");
	  Element result_node = createDocNode(id, rank);
	  document_list.appendChild(result_node);
	}
      }
      
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
    }
    
    result.appendChild (document_list);

    return result;
    

    
  }
	

}

