/**
 * pSHIELD
 * Service Composition
 *
 * @authors Davide Migliacci, Vincenzo Suraci
 * Department of System and Computer Science (DIS)
 * University of Rome "Sapienza"
 * Via Ariosto, 25
 * 00184, Rome, IT
 *
 * Updated on 19-May-2011
 * Version 1.0
 
 */


package eu.artemis.shield.composition.compositionmanager.impl;
/**
 
 * The present class shows how a pSHIELD OSGi component
 * could use the potentiality offered by the pSHIELD
 * Service Discovery Framework. It interfaces with the 
 * Generic Discovery Manager to discover the services
 * available in the (pSHIELD) network.  
 */
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

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

import eu.artemis.shield.composition.compositionmanager.ICompositionManager;
import eu.artemis.shield.discovery.gdm.interfaces.IGenericDiscovery;
import eu.artemis.shield.discovery.pdm.IService;
import eu.artemis.shield.discovery.pdm.IServiceList;
import eu.artemis.shield.discovery.pdm.IServiceProperty;
import eu.artemis.shield.discovery.pdm.IServicePropertyList;

public class CM implements ICompositionManager
{
  private BundleContext bc; 
  private CMGUI gui = null;
  
  private final int API_TYPE = 1;
  private final int IMPL_TYPE = 2;
  
  int SPD_lvl;

  private Vector impl_bundles_to_start = new Vector();
  
  private IGenericDiscovery gd = null;
       
  public CM(BundleContext bc)
  {
    this.bc = bc;
    
    gui = new CMGUI(bc,this);
    gui.setVisible(true);
                
  }

  
  /**
   * This function takes an Api Bundle symbolic name and create its Impl Bundle symbolic name.
   @param bundleApiName Symbolic API name
   @return Symbolic IMPL name
   */
  private String getBundleImplName(String bundleApiName)
  {
    
    int indexStart = bundleApiName.lastIndexOf(".");
    int indexEnd = bundleApiName.lastIndexOf("-");
    
    if (indexEnd > 0){
      
      String bundleImplName = bundleApiName.substring(indexStart+1, indexEnd"-IMPL";
      return bundleImplName;
      
    }
    else
      // SLP Bundle
      return bundleApiName.substring(1, bundleApiName.length()-1)+"-IMPL";
  }
  
  private Bundle installApiHashtable ht, String bundle_type, HashMap security ) {
    
    Vector implBundleNames = new Vector();
    
    String initial_impl_bundle = ((String) ( (Vectorht.get("Project Name")).elementAt(0))+"-IMPL";
    
    // Display counter
    int j = 1;
    
    // Get the imported api 
    Vector imp_packages = Vector ht.get"Import" );
    
    gui.append("\n");
    gui.appendts("Installing Import Api of " + initial_impl_bundle);
    
    boolean found = false;
    
    Iterator it = imp_packages.iterator();
    
    // For each import bundle
    while it.hasNext() ){
      
      found = false;
      
      String imp_package = String it.next();
      
      gui.append("\n");
      gui.appendts ("API : " + j++ + "/" + imp_packages.size() " - looking for : " + imp_package );
      
      // Get all installed bundles in the framework
      Bundle[] installed_bundles = bc.getBundles();
      
      if installed_bundles != null ) {
        
        // For each installed bundle
        for int i = 0; i < installed_bundles.length; i++ ) {
        
          Bundle temp = Bundle installed_bundles[i];
          
          Dictionary bundle_manifest = temp.getHeaders();
          
          // Check the exported bundles of the only API Bundles
          if bundle_manifest.get"Bundle-Category" != null &&  bundle_manifest.get"Bundle-Category" ).equals"API" ) ) {
            
            String imported_packages = String bundle_manifest.get"Export-Package" );
            
            // If there is an installed bundle that export the requested package, we can procede whit the other requested packages
            if imported_packages.containsimp_package ) ) {
              
              gui.appendts("The installed bundle [" + bundle_manifest.get"Bundle-Name" +"] exports the requested package.");
              
              String name_bundle_found = getBundleImplName( ( String bundle_manifest.get"Bundle-Symbolicname" ) );
                              
              if!name_bundle_found.equalsinitial_impl_bundle ) ){
                // Add the IMPL bundle Symbolic Name into the vector if security level is satisfied
                
                implBundleNames.addname_bundle_found );
              }
              
              found = true;
              
              break;
              
            }
            
          }
        }
        
        // If there aren't installed bundle that export the requested package, we need to search one in the SLP
        if !found ) {
          
          gui.appendts("There aren't installed bundles that export the requested package : " + imp_package);
          
          gui.appendts("Trying to search the package into the SLP...");
          
          Vector bundles = getBundleByExportedPackageimp_package, bundle_type, API_TYPE );
          
          ifbundles != null && !bundles.isEmpty()) {

              Iterator it2 = bundles.iterator();
            
              // Use the first package founded
            if it2.hasNext() ){
                
                Hashtable ht_tmp = (Hashtableit2.next();
                
                gui.appendts "Found : " + ht_tmp.get"Service Name" ).toString() );
                gui.appendts "> It exports : " + ht_tmp.get"Export" ).toString() "\n");
                
                Bundle tmp = installBundleht_tmp );
                
                if tmp != null ){
                  
                  gui.appendts tmp.getSymbolicName() " : Installation completed." );
                  
                  String name_bundle_found = getBundleImplNameht_tmp.get"Project Name" ).toString() );
                  
                  String service_name = ht_tmp.get("Project Name").toString().substring(1,ht_tmp.get("Project Name").toString().length()-);
                  
                  if!name_bundle_found.equalsinitial_impl_bundle ) ){
                  // Add the IMPL bundle Symbolic Name into the vector 
                    
                    if (security.containsKey(service_name)){
                      
                      gui.appendts("Security Level" + security.get("Cryptography"" " + security.get("Accounting""\n" );
                      
                      gui.appendts("Security Level" (Integer)ht_tmp.get("SPD Level"));
                      
                    }
                    
                    implBundleNames.addname_bundle_found );
                }
                  
                else {
                  
                  gui.appendts "ERR: Installation aborted !!!" );
                  
                }
                
              }
            
          else {
            
            gui.appendts "ERR: There aren't available bundles in SLP that export the " + imp_package + " package" );
            
          }

        }
        
        
      }
      
    }
     
    
    if installImplimplBundleNames, bundle_type, security ) ){

      gui.appendts initial_impl_bundle + " import installation completed.\n" );
    }
    else{
      gui.appendts initial_impl_bundle + " import installation aborted.\n" );
      return null;
    }
    
    Bundle bundle_inst = installBundleht );
    
    if bundle_inst != null ){
       
      gui.appendts "IMPL installed : " + ht.get"Service Name" ) );
      
    else {

      
      gui.appendts "IMPL installation ABORTED : " + ht.get"Service Name" ) );
      return null;
      
    }
    
    return bundle_inst;
    
  }
  
  /**
   * This function installs 
   @param bundles_name
   @param bundle_type
   @return
   */
  private boolean installImplVector bundles_name, String bundle_type, HashMap parameter ){
    
    Bundle tmp = null;   

    Iterator it = bundles_name.iterator();
    
    int y = 1;
    
    // For each import bundle
    while it.hasNext() ){

      boolean found = false;
      
      String impl_bundle =  String it.next()
      
      gui.appendts ("IMPL : " + y++ + "/" + bundles_name.size() " - looking for : " + impl_bundle );
      
      // Get all installed bundles in the framework
      Bundle[] installed_bundles = bc.getBundles();
      
      if installed_bundles != null ) {
        
        // For each installed bundle
        for int i = 0; i < installed_bundles.length; i++ ) {
          
          tmp = Bundle installed_bundles[i];
  
          Dictionary bundle_manifest = tmp.getHeaders();
          
          // Check the IMPL bundles
          if bundle_manifest.get"Bundle-Category" == null ) {
            
            String imp_bundles = String bundle_manifest.get"Bundle-Symbolicname" );
            
            // If there is an installed bundle that export the requested package, we can procede whit the other requested packages
            if imp_bundles != null && imp_bundles.containsimpl_bundle ) ) {
              
              gui.appendts "The installed bundle [" + bundle_manifest.get"Bundle-Name" +"] implements the requested bundle." );

              impl_bundles_to_start.addtmp );
              
            
            
            else found = false;
            
          }
          
        }
      }
        
      // If there aren't installed bundle that implements the requested package, we need to search one in the SLP
      if !found ) {
        
        gui.appendts "There aren't installed bundles that implements the requested package : " + impl_bundle );
        
        gui.appendts "Trying to search the package into the SLP...\n" );
        
        Vector bundles = getBundleByExportedPackageimpl_bundle, bundle_type, IMPL_TYPE );
        
        ifbundles != null && !bundles.isEmpty()) {
  
            Iterator it2 = bundles.iterator();
          
            // Use the first package founded
          if it2.hasNext() ){
              
              Hashtable ht = (Hashtableit2.next();
              
              gui.appendts "Found : " + ht.get"Service Name" ).toString() );
              gui.appendts "> It implements : " + impl_bundle + "\n");
              
              tmp = installApiht, bundle_type, parameter );
              
              if tmp != null ){
                
                gui.appendts "Installation completed." );
                impl_bundles_to_start.addtmp );
                              
              else {
                
                gui.appendts "ERR: Installation aborted !!!" );
                
                return false;
                
              }
              
            }
          
        else {
          
          gui.appendts "ERR: There aren't available bundles in SLP that implement the " + impl_bundle );
          
          return false;
        
        }
        
      }
    
    }
    
    return true;
    
  }
  
  /**
   * This function try to install and run a bundle from its jar url
   */
  
  public void runBundle Hashtable bundle_properties, int level, HashMap bundles){
    
    String bundle_name = String ) ( ( Vector bundle_properties.get"Service Name" ) ).elementAt(0;
    
    String bundle_jar_url = String ) ( ( Vector bundle_properties.get"Jar Url" ) ).elementAt(0;
    
    String bundle_type = String ) ( ( Vector bundle_properties.get"Type" ) ).elementAt(0);
    
    Integer bundle_security = (Integer)((Vectorbundle_properties.get("SPD Level")).elementAt(0);
    
    Bundle initial_bundle = installApi bundle_properties, bundle_type, bundles );
    
    Iterator it = impl_bundles_to_start.iterator();
    
    int y = 1;
    
    while it.hasNext() ){
      
      Bundle tmp =  Bundle it.next()
      gui.append"[" + y++ + "]" );
      try{
        
        tmp.start();
      
      catch (Exception e){
        
        gui.appendts "ERR: Run command aborted !!!" );
        System.out.println e.getMessage() );
      }
      
    }
    
    if initial_bundle != null ){
      
      Dictionary bundle_manifest = initial_bundle.getHeaders();
      
      Integer parameter = (Integer) ( ( Vector bundle_properties.get"SPD Level" ) ).elementAt(0);
      
      String s = (Stringbundle_manifest.get("Bundle-Name");
      
      gui.appendts "Initial bundle installed." );
      gui.appendts "Running : " + bundle_properties.get"Service Name" ) );
      
      try
      {
        initial_bundle.start();
      
      catch (Exception e){
        
        gui.appendts "ERR: Run command aborted !!!" );
        System.out.println e.getMessage() );
      }
                      
    else {
      
      gui.appendts "ERR: Run command aborted !!!" );
      
    }
     
  }
  
  /**
   * This function install the bundle using its JAR URL
   @param hashtable of the bundle to be installed
   @return the bundle if the installation is completed, null if the installation crashed
   */
  
  private Bundle installBundle Hashtable bundle ){
    
    // Try to install the selected bundle from its jar url
    try{
      
      gui.appendts("Trying to install "+ bundle.get"Service Name" +" from its JAR URL...")
      
      Bundle b = bc.installBundle( ( ( String ) ( (Vectorbundle.get"Jar Url" ) ).elementAt(0) ) );
      
      return b;
    
    }catch (Exception e){
      
      System.out.printlne.getCause() );
      return null;
    
    }
  }
  
  
  /**
   * @authors Davide Migliacci, Vincenzo Suraci
   
   * This function try to discover a bundle that exports the requested package
   
   @param package_name : The name of the requested package
   @param service_type : The service type
   @param bundle_type : The bundle type ( API or IMPL ) to find
   @return : A vector of Hashtables. One Hashtable for each founded bundle that exports the requested package. Return null if there are not bundles
   */
  private Vector getBundleByExportedPackage (String package_name, String service_type, int bundle_type)
  {
    Vector services = serviceDiscovery(service_type, null, false);                 
    Vector services_discovered = new Vector();
        
    if services != null && !services.isEmpty() ) {
      
      Iterator it = services.iterator();
        
      // For each API bundles available into the SLP 
        while it.hasNext() ){
          
          Hashtable ht = Hashtable it.next();
          
          switch bundle_type ) {
            case API_TYPE : 
              if (   ht.containsKey"Export" && ht.containsKey"Api" && ht.containsKey"Impl" )  
                  
                  // Search only into the simple API Bundles and the API&IMPL Bundles
                    && ( ( Boolean ) ( (Vectorht.get"Api" ) ).elementAt(0) ).booleanValue() 
                  
                ){  
                String exported_packages = String ) ( ( Vector ht.get"Export" ) ).elementAt(0;
                
                if exported_packages.containspackage_name ) ) {
                  
                  services_discovered.addht );
                  
                }
                }
              break;
            case IMPL_TYPE :
              if (   ht.containsKey"Export" && ht.containsKey"Api" && ht.containsKey"Impl" )  
                      
                    // Search only into the simple API Bundles and the API&IMPL Bundles
                      && ( ( Boolean ) ( (Vectorht.get"Impl" ) ).elementAt(0) ).booleanValue()
                      
                    ){  
                  
                  if ht.get"Project Name" != null ){
                
                    String implement_packages = String ) ( ( Vector ht.get"Project Name" ) ).elementAt(0;
                      
                      if implement_packages.equalspackage_name.substring(0, package_name.length()-) ) ) {
                        
                        services_discovered.addht );
                        
                      }
                  }
                    }
                break;
          }
        }
      
      }

    return services_discovered;
  }
  
  
  
  /**
   * This function find the service available for a specified service type and an array of keywords to do a better filter 
   @param type : the service type to search
   @param kw : an array of keywords to search
   @param filter_api : set false if you need all the available bundles, true if you want only the API bundles
   @return a vector of found services ( with no API bundles )
   */
  private Vector serviceDiscovery(String type, String[] kw, boolean filter_api)
  {      
    Vector discovered_services = new Vector();
    try
    {        
      
      gui.append("- Service discovery with a specified service type\n");
      ServiceReference[] gdmList = findGenericDiscoveryModuleImplementations();
      
      if (gdmList != null)
      {
        /*
         * We ignore the possbility to have more than one GDM implementation.
         * JUST USE THE FIRST ONE...
         */                  
        gd = (IGenericDiscoverybc.getService(gdmList[0]);
        
        /*
         * We are ready to start the discovery process!
         */        
        
        /*
         * Set an array of keywords, useful to better filter 
         * the services
         */  
        if kw == null 
          kw = new String[0];
          //kw[0]="gui=false";
        
        for(int i = 0; i<kw.length; i++)
        {
          gui.append("KEYWORDS " + i + "--> " + kw[i].toString() "\n");
        }
        /*
         * LET pSHIELD DISCOVER THE SERVICES
         */
        
        gui.append("- Looking for Services ...");
        
        // LinkedList ll = gd.findServices(vid, type, kw);
        String CDQL =         
          "SELECT default" +
          "FROM default" +
          "SERVICETYPE " + type +
          "LANGUAGE en_gb" +
          "WHERE " +
          "USING slp";
        String SPARQL = "";
        LinkedList query_output = null;
        IServiceList sl = gd.findServices(CDQL, SPARQL, query_output);        
        
        if sl != null )
        {           
          if sl.isEmpty() )
          {
            // NO SERVICES HAVE BEEN DISCOVERED
            gui.append("- No services found !");
          }
          else
          {                                        
              //Iterator it = sl.iterator();
              //while (it.hasNext())
              for (int i = 0; i < sl.size(); i++)
              {                
                try
                {
                  IService s = sl.getService(i);                  
                  String url = s.getOWLSURL();
                  System.out.println("service URL #" + i + " = " + url);
                  IServicePropertyList spl = s.getProperties();
                  Hashtable ht = new Hashtable();
                  for (int j=0; j<spl.size(); j++)
                  {
                    try
                    {
                      IServiceProperty sp = spl.getServiceProperty(j);
                      System.out.print("> attribute #" + j + " --> " + sp.getName() "=");
                      Vector values = sp.getValues();
                      if (values != null)
                      {
                        for (int k=0; k<values.size(); k++)
                        {
                          Object obj = values.get(k);
                          if (obj != null)
                          {
                            if (k > 0System.out.print(",");
                            System.out.print(obj.toString());                            
                          }
                        }
                      }
                      ht.put(sp.getName(), values);
                      System.out.println("");                      
                    }
                    catch (IndexOutOfBoundsException ioobe)
                    {
                      ioobe.printStackTrace();
                    }                    
                  }  
                  if (ht.containsKey("Impl"&& ht.containsKey("Api"))
                  {
                    if (filter_api
                    {                  
                      boolean has_impl = ((Boolean)((Vector)ht.get("Impl")).elementAt(0)).booleanValue();
                      if (has_impldiscovered_services.add(ht);                                  
                    }      
                    else
                    {
                      // Insert an HashTable for each discovered service
                      discovered_services.addht );                    
                    }
                  }                  
                }
                catch (IndexOutOfBoundsException ioobe)
                {
                  ioobe.printStackTrace();
                }                
              }              
          }
        }
        else
        {
          //NO SERVICES HAVE BEEN DISCOVERED
          gui.append("NO SERVICE FOUND!");          
        }
        
      }
      else
      {
        /*
         * It was not possible to find a suitable implementation of IGenericDiscovery 
         */
        gui.append("No Bundles implement the IGenericDiscovery interface!\n");        
      }            
    }    
    catch (Exception e)
    {
      /*
       * Something went wrong!
       * It was not possible to find a suitable implementation of IGenericDiscovery 
       */
      gui.append(e.getMessage());
      e.printStackTrace();      
    }
    return discovered_services;
  }
  
  /**
   @author Vincenzo Suraci
   
   * This function uses the internal OSGi service discovery to find a suitable implementation
   * of the IGenericDiscovery interface.
   */
  private ServiceReference[] findGenericDiscoveryModuleImplementations() throws Exception
  {
    ServiceReference[] gdmi = null;
    try 
    {
      gdmi = bc.getServiceReferences("eu.artemis.shield.discovery.gdm.interfaces.IGenericDiscovery"null);
    
    catch (Exception e
    {
      throw e;
    }
    return gdmi;
  }  
  
  public void exit()
  {
    /*
     * Close GUI
     */
    gui.setVisible(false);
    gui.dispose();
  }
}