/*
 *    IIIFPMH.java
 *    Copyright (C) 2019 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.core.GSException;
import org.greenstone.gsdl3.util.GSXML;
import org.greenstone.gsdl3.util.IIIFXML;
import org.greenstone.gsdl3.util.OID;
import org.greenstone.gsdl3.util.GSFile;
import org.greenstone.gsdl3.util.XMLConverter;

import org.greenstone.gsdl3.util.SimpleCollectionDatabase;
import org.greenstone.gsdl3.util.DBInfo;
// XML classes
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

// General Java classes
import java.io.File;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Set;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

/** Implements the IIIF service for GS3 collections.
 *  Digs into each collection's database to study the metadata
 *  and from that determine what file in a document's assocdir
 *  can be used to provide a source image for the doc
 *
 */

public class IIIFPMH extends ServiceRack {
  
  static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.IIIFPMH.class.getName());
  
  protected SimpleCollectionDatabase coll_db = null;
  
  protected String site_name = "";
  protected String coll_name = "";
 
  // set this up during configure
  protected Element list_sets_response = null;
      
  protected String index_stem = ""; // ****
  protected String infodb_type = "";
  
  /** constructor */
  public IIIFPMH() {
  }
  
  public void cleanUp() {
    super.cleanUp();//??
	
    if(this.coll_db != null) {		
	this.coll_db.closeDatabase();
	this.coll_db = null;
    }
  }
  
  /** configure this service 
  info is the IIIFPMH service rack from collectionConfig.xml, and 
  extra_info is buildConfig.xml */
  public boolean configure(Element info, Element extra_info) {
    if (!super.configure(info, extra_info)){
      logger.info("Configuring IIIFPMH(extends ServiceRack) returns false.");
      return false;
    }
    
    //get the names from ServiceRack.java
    this.site_name = this.router.getSiteName();
    this.coll_name = this.cluster_name;
    
    logger.info("Configuring IIIFPMH...");

    this.config_info = info;
    
    // the index stem is either specified in the buildConfig.xml file (extra_info) or uses the collection name
    Element metadata_list = (Element) GSXML.getChildByTagName(extra_info, GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);

    // Is indexStem needed for IIIF // ****
    if (metadata_list != null) {
	
      Element index_stem_elem = (Element) GSXML.getNamedElement(metadata_list, GSXML.METADATA_ELEM, GSXML.NAME_ATT, "indexStem");
      
      if (index_stem_elem != null) {
	this.index_stem = GSXML.getNodeText(index_stem_elem);
      }

      Element infodb_type_elem = (Element) GSXML.getNamedElement(metadata_list, GSXML.METADATA_ELEM, GSXML.NAME_ATT, "infodbType");
      if (infodb_type_elem != null) {
	this.infodb_type = GSXML.getNodeText(infodb_type_elem);
      }

    }

    if (index_stem == null || index_stem.equals("")) {
	this.index_stem = this.cluster_name; // index_stem is the name of the db in indext/text, it is <colname>.<db>
    }
    if (infodb_type == null || infodb_type.equals("")) {
      this.infodb_type = "gdbm"; // the default
    }

    Element get_record = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
    get_record.setAttribute(GSXML.NAME_ATT, IIIFXML.GET_RECORD);
    get_record.setAttribute(GSXML.TYPE_ATT, "iiif");
    this.short_service_info.appendChild(get_record);
    
    return true;
  }

  public boolean configureIIIF(Element iiif_config_elem) {
      
      // Open the coll db db databases and store handles to them	
    coll_db = new SimpleCollectionDatabase(infodb_type);
    if (!coll_db.databaseOK()) {
      logger.error("Couldn't create the collection database of type "+infodb_type);
      return false;
    }
    
    // Open databases for querying
    String coll_db_file = GSFile.collectionDatabaseFile(this.site_home, this.cluster_name, index_stem, infodb_type);
    if (!this.coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ)) {
      logger.error("Could not open collection database!");
      return false;
    }
    
    return true;
  }

  /** returns a specific service description */
  public Element getServiceDescription(Document doc, String service_id, String lang, String subset) {
      
    if (service_id.equals(IIIFXML.GET_RECORD)) {
      Element get_record = doc.createElement(GSXML.SERVICE_ELEM);
      get_record.setAttribute(GSXML.NAME_ATT, IIIFXML.GET_RECORD);
      get_record.setAttribute(GSXML.TYPE_ATT, "iiif");
      return get_record;
    }
    
    return null;
  }
    
  /** returns the actual record element used in the IIIF GetRecord response */
  protected Element processGetRecord(Element req) {
    /** arguments:
        identifier: required
     *  Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
     */ 
    NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
    HashMap<String, String> param_map = GSXML.getParamMap(params);    
    
    Document doc = XMLConverter.newDOM();
    
    String oid = param_map.get(IIIFXML.OID); // TODO should this be identifier???
    
    // Get a DBInfo object of the identifier; if this identifier is not present in the database,
    // null is returned.
    DBInfo info = this.coll_db.getInfo(oid);
    if (info == null) {
	logger.error("OID: " + oid + " is not present in the collection index database.");
	return IIIFXML.createErrorResponse(IIIFXML.ID_DOES_NOT_EXIST, "");
    }

    DBInfo top_level_info = null;

    int subsection_dot = oid.indexOf(".");
    
    if (subsection_dot > 0) {
	// OID specifies document sub-section
	// => Need to work out the top-level OID, as this is needed to retrieve assocfilepath
	String root_oid = oid.substring(0,subsection_dot);
	top_level_info = this.coll_db.getInfo(root_oid);
    }
    else {
	top_level_info = info;
    }
    
    
    // ****
    Element get_record_response = doc.createElement(GSXML.RESPONSE_ELEM);
    Element get_record = doc.createElement(IIIFXML.GET_RECORD);
    get_record_response.appendChild(get_record);
    Element record = doc.createElement(IIIFXML.RECORD);

    //compose the header element
    record.appendChild(createHeaderElement(doc, oid));      

    //compose the metadata element
    record.appendChild(createMetadataElement(doc, info, top_level_info));
    get_record.appendChild(record);
    return get_record_response;
  }
    
    
 /** create the metadata element used when processing GetRecord request
   */

  protected Element createMetadataElement(Document doc, DBInfo info, DBInfo top_level_info) {
    // the <metadata> element
    Element metadata_message = doc.createElement(IIIFXML.METADATA);

    addFirstMetadata(metadata_message, "assocfilepath", top_level_info);
    addFirstMetadata(metadata_message, "Image", info);
    
    return metadata_message;
  }

  /** a simple addMetadata where we look for meta_name metadata, and add as that name*/
    
  protected void addFirstMetadata(Element meta_list_elem, String meta_name, DBInfo info) {
      Vector<String> values = info.getMultiInfo(meta_name);
      if (values != null && values.size()>0) {
	  addMetadataElement(meta_list_elem, meta_name, values.get(0));
	  
      }
  }
    
    
  /** create the actual metadata element for the list */

  protected void addMetadataElement(Element meta_list_elem, String name, String value) {
    
    Element meta = GSXML.createTextElement(meta_list_elem.getOwnerDocument(), name, value);
    meta_list_elem.appendChild(meta);
  }


  /** create a header element used when processing requests like GetRecord
   */
  protected Element createHeaderElement(Document doc, String oid) {

        Element header = doc.createElement(IIIFXML.HEADER);
	
        Element identifier = doc.createElement(IIIFXML.IDENTIFIER);
	GSXML.setNodeText(identifier, coll_name + ":" + oid); // **** OID
        header.appendChild(identifier);

        return header;
  }

}


