/**
 * pSHIELD  (A4.2)
 * Service Discovery
 *
 @author Silvano Mignanti
 * Department of System and Computer Science (DIS)
 * University of Rome "Sapienza"
 * Via Ariosto, 25
 * 00184, Rome, IT
 *
 * phone: +39 329 11 38 610
 * email: silvano.mignanti@dis.uniroma1.it
 
 *
 * Created on 16-May-2007
 * Version 1.0
 
 */
package eu.artemis.shield.discovery.gdm.impl;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;

import java.net.MalformedURLException;
import java.net.URL;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import eu.artemis.shield.discovery.filter.IServicesFilter;
import eu.artemis.shield.discovery.gdm.impl.Const;
import eu.artemis.shield.discovery.gdm.interfaces.IGenericDiscovery;
import eu.artemis.shield.discovery.pdm.ISDParameter;
import eu.artemis.shield.discovery.pdm.IService;
import eu.artemis.shield.discovery.pdm.IServiceDiscovery;
import eu.artemis.shield.discovery.pdm.IServiceList;
import eu.artemis.shield.discovery.pdm.IServiceType;
import eu.artemis.shield.discovery.pdm.IServiceTypeList;
import eu.artemis.shield.discovery.qp.interfaces.IQueryPreprocessor;

public class CServiceDiscoveryGDM implements IGenericDiscovery {
  
  /*
   * PARAMTERS
   */
  private BundleContext bc;    

  /*
   * CONSTRUCTORS
   */
  public CServiceDiscoveryGDM(BundleContext bc) {
    
    this.bc = bc;
  }

  /*
   * IGENERICDISCOVERY METHODS 
   */  

  public LinkedList findServiceTypes(String VID)
  {
    if (Const.DEBUG_ENABLEDSystem.out.println("findServices Method of Generic Discovery Module");
    //ServiceReference[] srqpp = null;
    ServiceReference[] srSD = null;
    LinkedList result = new LinkedList();
                              
    try 
    {
      /*
       * FIND ALL THE PDMs
       */
      srSD = findPDMs();            
      for (int k = 0; k < srSD.length; k++
      {
        /*
         * FOR EACH PDM
         * FIND THE SERVICES USING THE CDQL QUERY
         */
        Object o = bc.getService(srSD[k]);
        //if (DEBUG) System.out.println(o2.getClass().getName() + srSD.length
        //    + "        " + k + "SDCDQL length: " + sdpcdql.length);
        IServiceDiscovery isDiscovery = (IServiceDiscoveryo;
        try 
        {
          IServiceTypeList temp = isDiscovery.findServiceTypes(new ISDParameter[0]);
          if (temp!=null)
          {
            if (Const.DEBUG_ENABLEDSystem.out.println("TEMP SIZE: " + temp.size());
            for(int j = 0; j<temp.size(); j++)
            {
              IServiceType sType = (IServiceType)temp.get(j);                  
              result.add(sType.toString());
            }
          }
        
        catch (Exception e
        {                
          System.out.println("PDM Exception!");
          e.printStackTrace();
        }
      }            
    
    catch (Exception e
    {
      System.out.println("No PDMs found");
      //e.printStackTrace();
    }                                
    return result;
  }
  

  public LinkedList findServices(String VID, String type, String[] keywords
  {
    if (Const.DEBUG_ENABLEDSystem.out.println("findServices Method of Generic Discovery Module");
    ServiceReference[] srqpp = null;
    ServiceReference[] srSD = null;    
    LinkedList owluri = new LinkedList();
    LinkedList result = new LinkedList();
    ISDParameter[] sdpcdql = null;
    
    try 
    {
      /*
       * FIND ALL THE QUERY PREPROCESSORS
       */
      srqpp = findQueryPreprocessors();
      
      String UserProfileQuery = "SELECT * WHERE{?x ?y ?z.}";
      String UserOWLQuery = "SELECT * WHERE{?x ?y ?z.}";
      String UserRequirementsOnServiceContext = getPreferencesOnUserRequirementsOnServiceContext(VID, type);
      
      for (int i = 0; i < srqpp.length; i++
      {
        /*
         * FOR EACH QUERY PREPROCESSOR
         * CREATE A QUERY
         */
        Object o = bc.getService(srqpp[i]);        
        Class[] ifs=o.getClass().getInterfaces();
        if (Const.DEBUG_ENABLED
        {
          for (int k =0; k<ifs.length; k++)
          {
            System.out.println("Name="+ifs[k].getName()+"\tCanName="+ifs[k].getName()+"\tHash"+ifs[k].hashCode());
          }
        }                  
        Class iqpp = IQueryPreprocessor.class;
        if (Const.DEBUG_ENABLEDSystem.out.println("Name="+iqpp.getName()+"\tCanName="+iqpp.getName()+"\tHash"+iqpp.hashCode());  
        IQueryPreprocessor iQPP = (IQueryPreprocessoro;        
        try 
        {
          /*
           * CREATE A QUERY
           */
          sdpcdql = iQPP.createQuery(VID, type, keywords);
          if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM::findservices --> Query created");                    

          try 
          {
            /*
             * FIND ALL THE PDMs
             */
            srSD = findPDMs();  
            if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM::findservices --> " + srSD.length + " PDM found!");
            for (int k = 0; k < srSD.length; k++
            {
              /*
               * FOR EACH PDM
               * FIND THE SERVICES USING THE CDQL QUERY
               */              
              IServiceDiscovery isDiscovery = (IServiceDiscoverybc.getService(srSD[k]);
              if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM::findservices --> PDM #" + k + " --> java type = " + isDiscovery.getClass().getSimpleName());
              try 
              {
                if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM::findservices --> PDM #" + k + " --> service discovering...");
                IServiceList temp = isDiscovery.findServices(sdpcdql);
                if (temp!=null)
                {  
                  if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM::findservices --> PDM #" + k + " --> discovered " + temp.size() " services!");
                  for(int j = 0; j<temp.size(); j++)
                  {
                    IService serv = (IService)temp.get(j);                  
                    String owlurl = serv.getOWLSURL();                  
                    owluri.add(owlurl);
                  }
                }
              
              catch (Exception e
              {                
                System.out.println("PDM Exception!");
                e.printStackTrace();
              }
            }            
          
          catch (Exception e
          {
            System.out.println("No PDMs found");
            //e.printStackTrace();
          }
        
        catch (Exception e
        {
          System.out.println("QPPException: " + e.getLocalizedMessage());
          e.printStackTrace();
        }            
      }
      
      /*
       * FILTER THE DISCOVERED OWL URLs
       * USING THE FILTER
       
       * NOTE: THIS IS JUST A WORKAROUND TO SOLVE ASAP THE FILTERING ISSUES
       * WE USED A MODIFIED INTERFACE INVENTED BY SILVANO WHICH IS NOT THE
       * STANDARD ONE!!!!!!!!
       */
                              
      if (Const.SEMANTIC_FILTER_ENABLED)
      {        
        try
        {
          // DISCOVER ALL THE LOCAL FILTERS
          ServiceReference[] filterSR = findFilters();
          
          // DISCOVER TAKE THE FIRST ONE
          IServicesFilter iSF = (IServicesFilterbc.getService(filterSR[0]);
          
          // LET FILTER SEMANTICALLY THE ALREADY DISCOVERED SERVICES 
          result = iSF.filterServices(owluri, VID, UserProfileQuery, UserOWLQuery, UserRequirementsOnServiceContext);
        }
        catch(Exception e)
        {
          System.out.println("Filter Exception: ");
          e.printStackTrace();
        }          
      }
      else
      {
        result = owluri; // We pass all the discovered services...
      }                        
    
    catch (Exception e
    {
      e.printStackTrace();
    }  
    
    return result;
  }
  

  /**
   @param CDQL the query expressed in CDQL sintax used for the first step of the discovery process
   @param SPARQL the query expressed in SPQRQL syntax used for the second step(filtering) of discovery process
   @param query_output a linkedlist of values,if any, returned by the execution of the filtering step.
   @return a list of service discovered and filtered on the besis of the two query submitted.
   */
  public IServiceList findServices(String CDQL, String SPARQL, LinkedList query_output) {
  
      /*
       * FIND ALL THE PDMs
       */
    LinkedList owluri = new LinkedList();
    LinkedList query_result = new LinkedList();
    IServiceList temp = null;
    
      ServiceReference[] srSD;
      try {
        srSD = findPDMs();
        for (int k = 0; k < srSD.length; k++
        {
          /*
           * FOR EACH PDM
           * FIND THE SERVICES USING THE CDQL QUERY
           */
          //sdpcdql = createQuery(CDQL);
          
          Object o2 = bc.getService(srSD[k]);
          if (Const.DEBUG_ENABLEDSystem.out.println(o2.getClass().getName() + srSD.length
              "        " + k + "CDQL length: " + CDQL.length());
          IServiceDiscovery isDiscovery = (IServiceDiscoveryo2;
          
            temp = isDiscovery.findServices(CDQL);
            if (temp!=null)
            {  
              if (Const.DEBUG_ENABLEDSystem.out.println("TEMP SIZE: " + temp.size());
              //il loop seguente should be eliminato
              for(int j = 0; j<temp.size(); j++)
              {
                IService serv = (IService)temp.get(j);  
                String owlurl = serv.getOWLSURL();                  
                owluri.add(owlurl);              
              }
            }
        }
      
      catch (Exception e
      {                
        System.out.println("PDM Exception!");
        e.printStackTrace();
      }            
                  
      if (Const.SEMANTIC_FILTER_ENABLED)
      {        
        try
        {
          // DISCOVER ALL THE LOCAL FILTERS
          ServiceReference[] filterSR = findFilters();
          if (Const.DEBUG_ENABLEDSystem.out.println("Filters found: " + filterSR.length);
          
          // DISCOVER TAKE THE FIRST ONE
          IServicesFilter iSF = (IServicesFilterbc.getService(filterSR[0]);
          if (Const.DEBUG_ENABLEDSystem.out.println("First filter selected");
          
          // LET FILTER SEMANTICALLY THE ALREADY DISCOVERED SERVICES 
          //result = iSF.filterServices(owluri, null, SPARQL, "");
          query_result=iSF.filterServices(temp, null, SPARQL, "");
        }
        catch(Exception e)
        {
          System.out.println("Filter Exception: ");
          e.printStackTrace();
        }
        
      }
      if(query_output!=null)
        query_output.addAll(query_result);
      if (Const.DEBUG_ENABLED && temp!=nullSystem.out.println("TEMP AFTER FILTER SIZE: " + temp.size());
      return temp;
  }
  

  public boolean registerService(String VID, String description, Hashtable parameters, long timeout, String type
  {
    /*
     * We use SLPTool...
     * > We ignore the VIDID
     * > We use the parameters hashtable to register the SLP parameters
     */
    
    if (Const.DEBUG_ENABLED)
    {
      System.out.println("CServiceDiscoveryGDM --> registerService");
      System.out.println("CServiceDiscoveryGDM --> URL description " + description);
      System.out.println("CServiceDiscoveryGDM --> String type " + type);
    }
    
    String[] args = new String[4];
    
    int index = 0;
    
    /*
     * REGISTER COMMAND
     */

    /*args[index] = "-debug";
    index++;
    */
    
    args[index"register";
    index++;    
    
    /*
     * SERVICE TYPE
     */
    if (type != null)
    {
      int i = type.lastIndexOf(":");
      if (i >= 0)
      {
        try
        {
          if (new URL(description).getProtocol().equalsIgnoreCase(type.substring(i+1)))
          {
            //args[index] = type + "://" + description.toString().substring(description.getProtocol().length());
            args[index= type + description.toString().substring(new URL(description).getProtocol().length());
          }
          else
          {
            /*
             * Service type is not compliant with the URL... Different protocols!?!
             * Register them separately...
             */ 
            
            args[index= type;
          }
        }catch(MalformedURLException ex){
            ex.printStackTrace();
          }
      }
      else
      {
        try
        {
          if (new URL(description).getProtocol().equalsIgnoreCase(type))
          {
            args[index= description.toString();
          }
          else
          {
            /*
             * Service type is not compliant with the URL... Different protocols!?!
             * Register them separately...
             */ 
            args[index= type;
          }
        }catch(MalformedURLException ex){
            ex.printStackTrace();
          }
      }
    }
    if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM--> serviceURL " + args[index]);
    index++;
    
    /*
     * ATTRIBUTES
     
     * Modified by Vincenzo Suraci on 08/09/2011
     * The OntologyURI parameter was automatically inserted in each service registration. 
     * Now the OntologyURI paramter is no longer automatically added.
     */    
    
    //args[index] = "(OntologyURI="+description.toString()+")";    
    args[index"";
    
    if (parameters != null)
    {
      Enumeration enukey = parameters.keys();
      while (enukey.hasMoreElements())
      {
        String attr_tag = (String)enukey.nextElement();
        if (args[index].length() 0)
          args[index+= ",(" + attr_tag;
        else          
          args[index+= "(" + attr_tag;
        Vector attr_values = (Vector)parameters.get(attr_tag);
        if (attr_values != null)
        {
          if (attr_values.size() 0)
          {
            args[index+= "=";
            for (int i = 0; i < attr_values.size(); i++)
            {
              if (i > 0args[index+= ",";
              Object o = attr_values.elementAt(i);
              if (o.getClass().getName().equals("java.lang.String"))
              {
                args[index+= (String)o;
              }
              else if (o.getClass().getName().equals("java.lang.Integer"))
              {
                args[index+= (Integer)o;
              }
              else if (o.getClass().getName().equals("java.lang.Boolean"))
              {
                Boolean b = (Boolean)o;
                if (b.booleanValue()) args[index+= "true"else args[2+= "false";
              }
              else
              {
                /*
                 * CONSIDER THIS VALUE AS A STRING...
                 */
                args[index+= o.toString();
              }
            }
          }
        }
        args[index+= ")";
      }
    }
    if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscoveryGDM--> attributes " + args[index]);
    index++;
    
    /*
     * TIMEOUT
     */
    int to = (int)timeout;
    args[index"" + to;
    
    /*
     * FIND ALL THE PDMs
     */
    try
    {
      ServiceReference[] srSD = findPDMs();            
      for (int k = 0; k < srSD.length; k++
      {
        /*
         * FOR EACH PDM
         * REGISTER THE SERVICES USING THE REGISTERSERVICE METHOD
         */
        Object o2 = bc.getService(srSD[k]);
        if (Const.DEBUG_ENABLED){ 
          System.out.print(o2.getClass().getName());
          System.out.print(srSD.length);
          System.out.println("        " + k);
          //System.out.println("SDCDQL length: " + sdpcdql.length);
        }
        IServiceDiscovery isDiscovery = (IServiceDiscoveryo2;
        try 
        {
          /*
           * If the service is registered at least with one PDM, than the
           * method exits successfully.
           */
          if (isDiscovery.registerService(null, args)) return true;                                        
        
        catch (Exception e
        {                
          System.out.println("PDM Exception!");
          e.printStackTrace();
        }
      }
    }  
    catch (Exception e)
    {
      System.out.println("CServiceDiscoveryGDM::registerService ERROR!!!");
      e.printStackTrace();
    }
    
    return false;
  }
  
  public boolean registerService (Hashtable parameters, long timeout, String type) {
    
    /*
     * We use SLPTool...
     * > We ignore the VIDID
     * > We use the parameters hashtable to register the SLP parameters
     */
    
    if (Const.DEBUG_ENABLED)
    {
      System.out.println("CServiceDiscoveryGDM --> registerService");
      System.out.println("CServiceDiscoveryGDM --> String type " + type);
    }
    
    String[] args = new String[4];
    
    int index = 0;
    
    /*
     * REGISTER COMMAND
     */

    /*args[index] = "-debug";
    index++;
    */
    
    args[index"register";
    index++;    
    
    /*
     * SERVICE TYPE
     */
    if (type != null)
    {
      args[index= type;      
    }
    if (Const.DEBUG_ENABLED
      System.out.println("CServiceDiscoveryGDM--> serviceURL " + args[index]);
    index++;
    
    /*
     * ATTRIBUTES
     */    
    if (parameters != null)
    {
      Enumeration enukey = parameters.keys();
      while (enukey.hasMoreElements())
      {
        String attr_tag = (String)enukey.nextElement();
        if (args[index]!= null)
          args[index+= ",(" + attr_tag;
        else 
          args[index"(" + attr_tag;
        Vector attr_values = (Vector)parameters.get(attr_tag);
        if (attr_values != null)
        {
          if (attr_values.size() 0)
          {
            args[index+= "=";
            for (int i = 0; i < attr_values.size(); i++)
            {
              if (i > 0args[index+= ",";
              Object o = attr_values.elementAt(i);
              if (o.getClass().getName().equals("java.lang.String"))
              {
                args[index+= (String)o;
              }
              else if (o.getClass().getName().equals("java.lang.Integer"))
              {
                args[index+= (Integer)o;
              }
              else if (o.getClass().getName().equals("java.lang.Boolean"))
              {
                Boolean b = (Boolean)o;
                if (b.booleanValue()) args[index+= "true"else args[2+= "false";
              }
              else
              {
                /*
                 * CONSIDER THIS VALUE AS A STRING...
                 */
                args[index+= o.toString();
              }
            }
          }
        }
        args[index+= ")";
      }
    }
    if (Const.DEBUG_ENABLED
      System.out.println("CServiceDiscoveryGDM--> attributes " + args[index]);
    index++;
    
    /*
     * TIMEOUT
     */
    int to = (int)timeout;
    args[index"" + to;
    
    /*
     * FIND ALL THE PDMs
     */
    try
    {
      ServiceReference[] srSD = findPDMs();            
      for (int k = 0; k < srSD.length; k++
      {
        /*
         * FOR EACH PDM
         * REGISTER THE SERVICES USING THE REGISTERSERVICE METHOD
         */
        Object o2 = bc.getService(srSD[k]);
        if (Const.DEBUG_ENABLED){ 
          System.out.print(o2.getClass().getName());
          System.out.print(srSD.length);
          System.out.println("        " + k);
          //System.out.println("SDCDQL length: " + sdpcdql.length);
        }
        IServiceDiscovery isDiscovery = (IServiceDiscoveryo2;
        try 
        {
          /*
           * If the service is registered at least with one PDM, than the
           * method exits successfully.
           */
          if (isDiscovery.registerService(null, args)) return true;                                        
        
        catch (Exception e
        {                
          System.out.println("PDM Exception!");
          e.printStackTrace();
        }
      }
    }  
    catch (Exception e)
    {
      System.out.println("CServiceDiscoveryGDM::registerService ERROR!!!");
      e.printStackTrace();
    }
    
    return false;
  }
  
  public boolean registerService(String description, Hashtable parameters, long timeout, String type
  {
    return registerService(null, description, parameters, timeout, type);
  }
  
  /*
   * ADDITIONAL METHODS 
   */

  public ServiceReference[] findQueryPreprocessors() throws Exception{
    ServiceReference[] srqpp = null;
    try {
      srqpp = bc.getServiceReferences("eu.artemis.shield.discovery.qp.interfaces.IQueryPreprocessor"null);
    catch (Exception e) {
      throw e;
    }
    return srqpp;
  }
  
  public ServiceReference[] findPDMs() throws Exception{
    ServiceReference[] srpdms = new ServiceReference[0];
    try {
      srpdms= bc.getServiceReferences("eu.artemis.shield.discovery.pdm.IServiceDiscovery"null);
    catch (Exception e) {
      throw e;
    }
    return srpdms;
  }
  
  public ServiceReference[] findFilters() throws Exception{
    ServiceReference[] srfilters = null;
    try {
      srfilters= bc.getServiceReferences("eu.artemis.shield.discovery.filter.interfaces.IServicesFilter"null);
    catch (Exception e) {
      throw e;
    }
    return srfilters;
  }
  
  
  private String getPreferencesOnUserRequirementsOnServiceContext(String VID, String type)
  {
    /*
     * This function takes the VID and the Service Type to look for the User Preferences
     * on the Service Context, regarding that specific Service Type. 
     */
    
    /*
     * The service type is in the form:
     * service:x.y.z:protocol
     
     * The pSHIELD preference manager wants only simple types
     *  so we take the "z" from the String type
     */  
    if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscvoeryGDM::getPreferencesOnUserRequirementsOnServiceContext --> Service Type = " + type);
    if (type.indexOf("service:">= 0)
    {
      // Delete "service:"
      type = type.substring(8);
    }
    int index = type.indexOf(":");
    if (index >= 0)
    {
      // Delete the protocol
      type = type.substring(0,index);
    }
    index = type.indexOf(".");
    while (index >= 0)
    {
      type = type.substring(index+1);
      index = type.indexOf(".");
    }
    if (Const.DEBUG_ENABLEDSystem.out.println("CServiceDiscvoeryGDM::getPreferencesOnUserRequirementsOnServiceContext --> Service Type = " + type);
    
    /*
     * TEMPORARLY DISABLED
     */
    ArrayList ar = new ArrayList();
    ar.add("maxdistance");
    ar.add("maxqueuetime");
    //requestOutcomes(VID, "servicediscovery42", type, ar, null);
    
    return "";
  }
}