/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.dnetlib.espas.util;

import java.io.IOException;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/** The Transformation handler is used for accessing static methods providing the required XML transformation functionality.
 * 
 * @author georgeathanasopoulos
 */
public class HarvestTransformationHandler {
   private static final String ESPAS_NAMESPACE="http://schemas.espas-fp7.eu/2.1";
   private static final String XLINK_NAMESPACE="http://www.w3.org/1999/xlink";
    
   private static Logger _logger = Logger.getLogger(HarvestTransformationHandler.class);

// self reference used for implenenting a singleton pattern 
   private static HarvestTransformationHandler selfReference;
  
//    bean properties related to the connection with the db instance that will be used for storing the harvested information
   private String databaseURL = "jdbc:postgresql://dl121.madgik.di.uoa.gr:5432";
   private String databaseName = "dnet_espas_v2";
   private String databaseUsername = "dnet";
   private String databasePassword = "dnetPwd";
            
   public HarvestTransformationHandler(){       
      if(selfReference==null)
         selfReference = this;
   }
    
   public static String serializeNode(Node extentContent) throws IOException {
         String nodeStr = MetadataHandler.serializeNode(extentContent).replaceAll("<\\?xml.*\\?>", "").trim();
         _logger.debug("Node serialization outcome :\n"+nodeStr);
         return nodeStr;
     }

   /** Returns an id that is a combination of the provided main entityId and the hash code of the following string value
     * :<arg i>+'_'+<arg i+1>
     * 
     * @note: this implementation migh be used in other cases as well, e.g. geo and vert extent; need to check this.
     */
    public static String getLocationId(String mainEntityId, String arg1, String arg2, String arg3){
        String locationId="";
        
//      check if arguments are empty or null
        if(arg1!=null && !arg1.isEmpty())
            locationId+="_"+arg1;

        if(arg2!=null && !arg2.isEmpty())
            locationId+="_"+arg2;
        
        if(arg3!=null && !arg3.isEmpty())
            locationId+="_"+arg3;

//        trim the location string from blank characters and preeding '_'
        locationId= locationId.replaceFirst("_", "");        
        locationId.trim();
//        calculate hash 
        locationId = ""+locationId.hashCode();

//        append main entity id in the begining if it's provided
        if(mainEntityId!=null && !mainEntityId.isEmpty())
            locationId=mainEntityId+locationId;
        
        return locationId;
    }
    

   /** Returns an id that is a combination of the provided main entityId and the hash code of the following string value
     * :<arg i>+'_'+<arg i+1>.
     * 
     */
   public static String getResourceId(String mainEntityId, String arg1, String arg2, String arg3){
        String resourceId="";
        
//      check if arguments are empty or null
        if(arg1!=null && !arg1.isEmpty())
            resourceId+="_"+arg1;

        if(arg2!=null && !arg2.isEmpty())
            resourceId+="_"+arg2;
        
        if(arg3!=null && !arg3.isEmpty())
            resourceId+="_"+arg3;

//        trim the location string from blank characters and preeding '_'
        resourceId= resourceId.replaceFirst("_", "");        
        resourceId.trim();

//        append main entity id in the begining if it's provided
        if(mainEntityId!=null && !mainEntityId.isEmpty())
            resourceId=mainEntityId+resourceId;
        
        return resourceId;
    }
    
   public static String getSourceId(String description){
        _logger.log(Priority.INFO, "getSourceId" + description);
        
        return ""+description.hashCode();
    }
       
   /** Calculates the id of the related party record using as input the provided role and the associated party ids.
     */
   public static String getResponsiblePartyId(String mainObjectId, Node relatedPartyInfoNode) {
        return mainObjectId+"_"+getResponsiblePartyId(relatedPartyInfoNode);
    }
    
   public static String getResponsiblePartyId(Node relatedPartyInfoNode) {
            String relatedPartyId = "";
        try {
            if ((relatedPartyInfoNode instanceof Element) && ((Element) relatedPartyInfoNode).getLocalName().equalsIgnoreCase("ESPAS_ResponsiblePartyInfo")) {
                Element role = (Element) ((Element) relatedPartyInfoNode).getElementsByTagNameNS(ESPAS_NAMESPACE,"role").item(0);
                String roleId = role.getAttributeNS(XLINK_NAMESPACE,"href");

                NodeList parties = ((Element) relatedPartyInfoNode).getElementsByTagNameNS(ESPAS_NAMESPACE,"party");
                String[] partyIds = new String[parties.getLength()];
                for(int i=0;i<parties.getLength();i++)
                    partyIds[i]=((Element)parties.item(i)).getAttributeNS(XLINK_NAMESPACE,"href").hashCode()+"";
                
//              calculate relatedPartyId
                relatedPartyId = roleId;
                for(String partyId:partyIds)
                    relatedPartyId+="_"+partyId;
            }
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
        }
        finally {
            return relatedPartyId;
        }
    }
    
   public static String getResponsiblePartyExtentKey(Boolean hasTemporal, String temporalKey, Boolean hasVertical, Boolean hasPlain, String geovertExtentKey, String description){
        String  extentKey= "extent";
        if(hasTemporal)
            extentKey += "_"+temporalKey.hashCode();
        if(hasVertical || hasPlain)
            extentKey += "_"+geovertExtentKey.hashCode();
        if(description!=null && !description.isEmpty())
            extentKey += description.hashCode();
        
        return extentKey;
    }
    
//////////////////////////////////////////
//              Geometry/Geography RELATED OPERATIONS
//          Forwarded to the GeometryTransfromationHandler

    /** Asserts whether the provided extent node has geographic extensions.  
     */
   public static boolean hasGeoExtent(Node extentContent){
        _logger.log(Priority.INFO, "hasGeoExtent");        
        try {
           if(extentContent==null){
              _logger.debug("GeoExtent is null");
              return false;
           }
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
            return GeometryMetadataHandler.hasGeoExtent(extentContent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return false;
        }
   }

   public static String getGeoExtent(Node geographicExtent){
     _logger.log(Priority.INFO, "getGeoExtent");
        try {           
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
            return GeometryMetadataHandler.getGeoExtent(geographicExtent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
   }

   /** Asserts whether the provided extent node has vertical extensions.  
     */
   public static boolean hasVerticalExtent(Node extentContent){
        _logger.log(Priority.INFO, "hasVerticalExtent");
            if(extentContent==null){
              _logger.debug("Vertical Extent is null");
              return false;
           }

            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
        return GeometryMetadataHandler.hasVerticalExtent(extentContent);
    }

   public static String getVerticalExtentMin(Node verticalExtent){
        _logger.log(Priority.INFO, "getVerticalExtentMin");
        try {
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
            return GeometryMetadataHandler.getVerticalExtentMin(verticalExtent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
    }

   public static String getVerticalExtentMax(Node verticalExtent){
        _logger.log(Priority.INFO, "getVerticalExtentMax");
        try {
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
            return GeometryMetadataHandler.getVerticalExtentMax(verticalExtent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
    }
    
   public static String getVerticalSRSName(Node verticalExtent) {
        _logger.log(Priority.INFO, "getVerticalSRSName");
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
        return GeometryMetadataHandler.getVerticalSRSName(verticalExtent);
    }
    
   public static String getGeometryLocation(Node geometryLocation){
        _logger.log(Priority.INFO, "getGeometryLocation");
        try {
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
            return GeometryMetadataHandler.getGeoLocation(geometryLocation);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
        
    }
    
   public static String getGeometrySrsName(Node geometryLocation){
        _logger.log(Priority.INFO, "getGeometrySrsName");
            GeometryMetadataHandler.initHandler(selfReference.databaseURL,selfReference.databaseName, selfReference.databaseUsername, selfReference.databasePassword);
        return GeometryMetadataHandler.getGeoSrsName(geometryLocation);
    }
    
    
    
    
//////////////////////////////////////////
//              TEMPORAL RELATED OPERATIONS
//          Forwarded to the TEmporalTransfromationHandler
    
    /** Asserts whether the provided extent node has temporal extensions.  
     */
    public static boolean hasTemporalExtent(Node extentContent) {
        try {
            _logger.log(Priority.INFO, "hasTemporalExtent");
            if(extentContent==null){
              _logger.debug("Temporal Extent is null");
              return false;
           }

            return TemporalMetadataHandler.hasTemporalExtent(extentContent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return false;
        } 
    }
    
    
    /** Extracts a serialized representation of the temporal extent start time. To extract this information an instance of the Geotoolkit TemporalExtent 
     * class is created and used for the unmarshalling and marshalling of the provided XML input. 
     */
    public static String getTemporalExtentStart(Node temporalExContent) {
        try{
        _logger.log(Priority.INFO, "getTemporalExtentStart");
        return TemporalMetadataHandler.getTemporalExtentStart(temporalExContent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        } 
    }
 
        
    /** Extracts a serialized representation of the end time stamp. To extract this information an instance of the Geotoolkit TemporalExtent 
     * class is created and used for the unmarshalling and marshalling of the provided XML input. 
     */
    public static String getTemporalExtentEnd(Node temporalExContent) {
        try{
        _logger.log(Priority.INFO, "getTemporalExtentDuration");
        return TemporalMetadataHandler.getTemporalExtentEnd(temporalExContent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        } 
    }

    /**
     * Extracts the content of the time instant set normally in the result time element of an observation. The function returns either the specified
     * time instant of an empty string in case of failure.
     */
    public static String getOResultTime(Node timeInstantContent) {
        _logger.log(Priority.INFO, "getOResultTime");
        try {
            return TemporalMetadataHandler.getTimeInstant(timeInstantContent);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
    }

    public static String getOValidTimeStart(Node timePeriodType){
        _logger.log(Priority.INFO, "getOValidTimeStart");
        try {
            return TemporalMetadataHandler.getTimePeriodStart(timePeriodType);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
        
    }

    public static String getOValidTimeEnd(Node timePeriodType){
        _logger.log(Priority.INFO, "getOValidTimeEnd");
        try {
            return TemporalMetadataHandler.getTimePeriodEnd(timePeriodType);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
        
    }

    
    /**@todo should be updated to facilitate gml:TimeObject features that are not addressed through timeperiod elements
     */
    public static String getOPhenomenonTimeStart(Node timePeriodType){
        _logger.log(Priority.INFO, "getOPhenomenonTimeStart");
        try {
            return TemporalMetadataHandler.getTimePeriodStart(timePeriodType);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
    }

    /**@todo should be updated to facilitate gml:TimeObject features that are not addressed through timeperiod elements
     */
    public static String getOPhenomenonTimeEnd(Node timePeriodType) {
        _logger.log(Priority.INFO, "getOPhenomenonTimeEnd");
        try {
            return TemporalMetadataHandler.getTimePeriodEnd(timePeriodType);
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }

    }

    public static String getVectorRepresentationId(Node vectorRepresentation){
        _logger.log(Priority.INFO, "getVectorRepresentationId");
        try {
//           @todo: add code here
            String vectorRepresentationStrippedContent  =serializeNode(vectorRepresentation);
            return "VID_"+vectorRepresentationStrippedContent.hashCode();
        } catch (Exception ex) {
            _logger.log(Priority.DEBUG, null, ex);
            return "";
        }
       
    }  
    
    public static String getComputationKind(String type){
//       @todo fix this problem
       String result = MetadataHandler.getComputationKind(type);
       return result;
    }
/////////////////////////////////////////////////////////////////////////////////////////////////////
//          Bean initilization code
   public String getDatabaseURL() {
      return databaseURL;
   }

   public void setDatabaseURL(String databaseURL) {
      this.databaseURL = databaseURL;
   }

   public String getDatabaseUsername() {
      return databaseUsername;
   }

   public void setDatabaseUsername(String databaseUsername) {
      this.databaseUsername = databaseUsername;
   }

   public String getDatabasePassword() {
      return databasePassword;
   }

   public void setDatabasePassword(String databasePassword) {
      this.databasePassword = databasePassword;
   }

   public String getDatabaseName() {
      return databaseName;
   }

   public void setDatabaseName(String databaseName) {
      this.databaseName = databaseName;
   }    
}
