/*
 *    Visualiser.java
 *    Copyright (C) 2004 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;

import org.greenstone.gsdl3.util.*;
import org.w3c.dom.Document;
import org.w3c.dom.Node; 
import org.w3c.dom.Element; 
import org.w3c.dom.Text; 
import java.util.Vector;
import java.util.HashMap;
import java.io.File;
import java.io.*;
import vishnu.server.*;
import vishnu.server.Search.*;
import vishnu.datablock.*;
import vishnu.util.Base64;

public class Visualizer
    extends ServiceRack {
    
    // the services on offer
    private static final String VIS_SERVICE = "VisApplet";

    // other internal strings
    private static final String ENGINE_TYPE_ELEM = "engineType";
    private static final String LUCENE_ENGINE = "LUCENE";
    private static final String MG_ENGINE = "MG";

    private Element applet_description = null;
    
    private String engine_type = LUCENE_ENGINE; // lucene is default
    private String collection_home = null;
    private SearchInterface engine = null;
    public Visualizer () {

    }
    public boolean configure(Element info, Element extra_info)
    {
	if (!super.configure(info, extra_info)){
	    return false;
	}

	Element e = this.doc.createElement(GSXML.SERVICE_ELEM);
	e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_APPLET);
	e.setAttribute(GSXML.NAME_ATT, VIS_SERVICE);
	short_service_info.appendChild(e);

	applet_description = this.doc.createElement(GSXML.SERVICE_ELEM);
        applet_description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_APPLET);
        applet_description.setAttribute(GSXML.NAME_ATT, VIS_SERVICE);


	String app_info = "<"+GSXML.APPLET_ELEM+" CODEBASE='lib' CODE='vishnu.testvis.visual.VishnuSingle.class' ARCHIVE='vishnu.jar,gsdl3.jar,xercesImpl.jar,xml-apis.jar' WIDTH='1000' HEIGHT='800'>";
	app_info += "<PARAM NAME='library' VALUE=''/>"; // filled in by receptionist
	app_info += "<PARAM NAME='viscgi' VALUE='?";
	app_info += GSParams.ACTION +"=a&amp;"+GSParams.REQUEST_TYPE +"=r&amp;"+GSParams.SERVICE+"="+VIS_SERVICE+"&amp;"+GSParams.OUTPUT+"=xml&amp;"+GSParams.RESPONSE_ONLY+"=1'/>";
	app_info += "<PARAM NAME='collection'   VALUE='" + this.cluster_name + "'/>";
	app_info += "<PARAM NAME='engine'   VALUE='GSDLEngine' />";
	// add view info if appropriate
	app_info += "The visualization applet.</"+GSXML.APPLET_ELEM+">";
	   
	Document dom = converter.getDOM(app_info);
	if (dom == null) {
	    System.err.println("Visualizer.configure Error: Couldn't parse applet info");
	    return false;
	}
	Element app_elem = dom.getDocumentElement();
	applet_description.appendChild(this.doc.importNode(app_elem, true));

	// get engine type from config file
	Element engine_elem = (Element)GSXML.getChildByTagName(info, ENGINE_TYPE_ELEM);
	if (engine_elem != null) {
	    engine_type = engine_elem.getAttribute(GSXML.NAME_ATT);
	    if (engine_type.equals("")) {
		engine_type = LUCENE_ENGINE;
	    }
	}
	
	collection_home = this.site_home + File.separator + "collect"+ File.separator + this.cluster_name + File.separator;
	if (engine_type.equals(LUCENE_ENGINE)) {
	    // make the full path the arg to MGSearcher??
	    engine = new LUCSearcher(collection_home, null);
	} else if (engine_type.equals(MG_ENGINE)) {
	    engine = new MGSearcher(collection_home, this.cluster_name, null);
	} else {
	    System.err.println("Visualiser: invalid engine type: "+engine_type);
	    return false;
	}
	return true;
    }	
    
    
    
    protected Element getServiceDescription(String service, String lang, String subset) {
	if (!service.equals(VIS_SERVICE)) {
	    return null;
	}
	Element describe = (Element) applet_description.cloneNode(true);
	describe.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
							    getTextString(VIS_SERVICE+".name", lang)));
	describe.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
							    getTextString(VIS_SERVICE+".description", lang)));
	return describe;
    }
    
    protected Element processVisApplet(Element request)
    {

	Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
	HashMap params = GSXML.extractParams(param_elem, false);

	String type = (String)params.get("type");

	// the result element
	Element result = this.doc.createElement(GSXML.RESPONSE_ELEM);
	result.setAttribute(GSXML.FROM_ATT, VIS_SERVICE);
	result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);

	// applet result info must be in appletInfo element
	Element applet_data = this.doc.createElement(GSXML.APPLET_DATA_ELEM);
	result.appendChild(applet_data);
	Element vis_data = this.doc.createElement("visData");
	applet_data.appendChild(vis_data);

	String results = "";
	if (type.equals("search")) {

	    //Element cluster_results = this.doc.createElement("cluster");
	    //vis_data.appendChild(cluster_results);
	    //Element description_results = this.doc.createElement("descriptions");
	    //vis_data.appendChild(description_results);

	    String query = (String)params.get("q");
	    System.err.println("the query was "+query);
	    
	    Vector doc_nums = new Vector();
	    Vector descriptions = new Vector();
	    
	    engine.search(query);
	    doc_nums = engine.getDocIdentifiers();
	    descriptions = engine.getDocDescriptions();
	    
	    DataBlock db = generateDataBlock(collection_home, null, doc_nums, descriptions);
	    System.err.println("got back data, now converting to string");
	    try {
		results = Base64.encodeObject(db);
	    } catch (Exception e) {
		System.err.println("trying to base64 encode the datablock, but exception happened: "+e);
	    }
	    System.err.println("after converting to string");
// 	    // serialise the data block
// 	    try {
// 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 		ObjectOutputStream oos = new ObjectOutputStream(baos);
// 		oos.writeObject(db);
// 		oos.close();
		
// 		//results = baos.toString("UTF-8");
// 		results = Base64.encode(baos.toByteArray());
// 	    } catch (Exception e) {
// 		System.err.println("Visualizer serialise data block error: "+e);
// 	    }
	    //System.err.println("num docs = "+doc_nums.size());
	    //Cluster c = new Cluster();
	    //results = c.getCluster(collection_home,doc_nums);
	    //results.trim(); 
	    //System.err.println("results = "+results);

	    Text t = this.doc.createTextNode(results);
	    vis_data.appendChild(t);

// 	    for (int i=0; i<descriptions.size(); i++) {
// 		String d = (String)descriptions.get(i);
// 		Element de = GSXML.createTextElement(this.doc, "desc", d);
// 		description_results.appendChild(de);
// 	    }
	    System.err.println("end of search");
	} else if (type.equals("fetch")) {
	    String docNum = (String)params.get("d");
	
	    Vector doc = engine.getDocContent(Integer.parseInt(docNum));
  	    for (int i=0; i<doc.size(); i++) {
		results += (String)doc.get(i) +"\n";
	    }
	    Text t = this.doc.createTextNode(results);
	    vis_data.appendChild(t);
	} else {
	    System.err.println("invalid type sent to Visualiser process: "+type);
	    return result;
	}
	System.err.println("returning result");
	return result;
    }

    private DataBlock generateDataBlock(String collection_home, String view, Vector docNums, Vector descriptions) {

	if (docNums.size()==0) {
	    return null;
	}
	DataBlock data = new DataBlock();

	CKServer ck_server = new CKServer(collection_home, view);
	
	/**** set data fields one by one and pass one what ever gets assembled ****/
		
	try{
	    
	    ck_server.setDescriptions(descriptions);
	    
	    /**** get candidate keywords ****/
	    
	    String[] keywords = ck_server.computeKeywords(docNums);
	    
	    data.words = keywords;
	    
	    
	    /**** get sparse document * keyword matrix ****/
	    
	    SparseMatrix matrix = ck_server.getSparseMatrix(docNums);
	    
	    data.matrix = matrix;
	    
	    
	    /**** get document indices, this is a subset of the original ****/
	    /**** those without keywords are excluded                    ****/
	    
	    int[] docs = ck_server.getHitDocuments();
	    
	    data.docs = docs;
	    
	    
	    String[] desc = ck_server.getHitDescriptions();
	    
	    data.descriptions = desc;
	    
	    
	    /**** get 10 or so clusters ****/
	    
	    Vector[] clusters = ck_server.getClusters();
	    
	    data.clusters = clusters;
	    
	    
	    double[][] centroids = ck_server.getCentroids();
	    
	    
	    /**** send their centroids through sammon mapping ****/
	    
	    Point2D[] sammon = ck_server.getSammonMap(centroids);
	    
	    System.err.println("Num descriptions: " + descriptions.size());
	    System.err.println("Num docs: " + docs.length);
	    System.err.println("Num desc: " + desc.length);
	    
	    data.sammon = sammon;
	    System.err.println("end of try in generatedatablock");
	} catch (Exception e) {
	    System.err.println("VisServlet: computing clustering Error: "+e);
	    e.printStackTrace();
	}
	ck_server = null;
	System.err.println("returning data");
	return data;
    }

}











