/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

package monitoring.web;

import ilog.cpl.datasource.IlpDataSource;
import ilog.cpl.datasource.IlpDefaultDataSource;
import ilog.tgo.faces.service.IltFacesDefaultContext;
import ilog.views.util.IlvLocaleUtil;
import integration.DataSourceProvider;
import integration.IntegrationRequest;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

import monitoring.generator.AbstractAlarmGenerator;
import monitoring.shared.LoggingUtils;
import monitoring.shared.ServicesUtils;
import monitoring.web.model.integration.IntegratedDataModelProvider;
import monitoring.web.model.integration.filters.OriginFilter;
import monitoring.web.oss.AbstractIntegrationProvider;
import monitoring.web.utils.Lock;
import monitoring.web.utils.WebUtils;
import shared.ResourceUtils;

/**
 * Context object used to maintain references to objects that are needed by the 
 * sample. It also triggers the sample initialization process of most entities 
 * involved.
 */
abstract public class AbstractSampleContext {

  protected String sampleTitle;
  protected SampleDataStructures dataStructures;
  protected SampleActionProviders actionProviders;
  protected SampleControls controls;
  protected DataSourceProvider dataSourceProvider;
  protected ServicesUtils servicesUtils;
  protected AbstractIntegrationProvider integrationProvider;
  protected IltFacesDefaultContext tgoContext;
  protected AbstractAlarmGenerator alarmGenerator;

  /**
   * This is a lock object that is used to ensure that 
   * server side task completion can be communicate to
   * clients.
   */
  public final Lock COMPLETION_LOCK = new Lock();
  
  /**
   * Creates the needed structures. 
   */
  public AbstractSampleContext() {
    //Initialize logger so that all can use it...
    initializeLogger();

    //Find the beans that provide the underlying configuration and resources
    //to the AbstractSampleContext

    setTgoContext(WebUtils.getTGOContext());
    
    setIntegrationProvider(WebUtils.getIntegrationProvider());

    //We must initialize it here as this is needed in the creation of the 
    //other service providers
    initializeIntegrationProvider();

    setDataSourceProvider(WebUtils.getDataSourceProvider());
    setServicesUtils(WebUtils.getServicesUtils());

    //Wait until the integration request handler has processed
    //all current requests before continue.
    Map<String,Object> params = new HashMap<String,Object>();
    COMPLETION_LOCK.executingTask = true;
    Runnable runme = new Runnable() {
      public void run() {
        synchronized(COMPLETION_LOCK) {
          COMPLETION_LOCK.executingTask = false;
          COMPLETION_LOCK.notify();
        }
      }
    };
    params.put(IntegrationRequest.ParamKey.EXTRA_KEY, runme);
    AbstractIntegrationProvider provider = getIntegrationProvider();
    provider.getIntegrationDispatcher().dispatch(IntegrationRequest.Type.RUNNABLE,provider.getUserIdentifier(), params, null);
    synchronized(COMPLETION_LOCK) {
      if (COMPLETION_LOCK.executingTask) try {
        COMPLETION_LOCK.wait();
      } catch (InterruptedException e) {
        LoggingUtils.getSampleLogger().log(Level.SEVERE, "Failed to initialize the sample context with exception:\n"+e.getLocalizedMessage());
        e.printStackTrace();
      }
    }
    initialize();
  }

  //////////////////////////////////////////////////////////////////////////////
  //Initialization
  //////////////////////////////////////////////////////////////////////////////

  /**
   * Initializes the context and all its resources.
   */
  protected void initialize() {

    //Initialize bundle so that all can use it...
    initializeResourceBundle();

    //Create underlying service providers
    dataStructures = new SampleDataStructures();
    controls = new SampleControls();
    actionProviders = new SampleActionProviders();

    //Initialize all service providers
    
    //It needs to be in this order as some service providers
    //depend on the initialized state of the others. See methods' comments.

    //1st ActionProviders 
    initializeActionProviders();

    //2nd DataStructures
    initializeDataStructures();

    //3rd Controls
    initializeControls();

    //4th alarm generator
    initializeAlarmGenerator();
    
    //5th Add some alarms for the sake of display
    createSampleAlarms();
  }

  /**
   * Initializes the alarm generator as this is OSS specific.
   */
  abstract protected void initializeAlarmGenerator();

  /**
   * Should return the name of the logger to be used by the sample.
   */
  abstract protected String getSampleLoggerName();

  /**
   * Initializes the <code>IntegrationProvider</code> used by the sample.
   */
  protected void initializeIntegrationProvider() {
    
    //We just need to set the user identifier for the IntegrationProvider
    integrationProvider.setUserIdentifier(WebUtils.getNextWebUserIdentifier());
  }
  
  /**
   * Initializes the <code>ResourceBundle</code> used by the sample.
   */
  protected void initializeResourceBundle() {

    //Default to this title
    sampleTitle = "Rogue Wave JViews TGO - Network And Service Monitoring (Ajax)";

    ResourceUtils.initResources(IlvLocaleUtil.getCurrentLocale(),
        SampleConstants.SAMPLE_RESOURCE_BUNDLE,
        sampleTitle);

    //Get the localized sample title
    sampleTitle = ResourceUtils.getString("titleSample");
  }


  /**
   * Initializes the <code>Logger</code> used by the sample.
   */
  protected void initializeLogger() {
    LoggingUtils.setSampleLogger(LoggingUtils.createLogger(getSampleLoggerName()));
  }

  /**
   * Initializes the data structures used by the sample. Depends on the action 
   * providers being initialized. 
   */
  protected void initializeDataStructures() {

    dataStructures.initialize(this);

    ////////////////////////////////////////////////////////////////////////////
    //DataSources
    ////////////////////////////////////////////////////////////////////////////

    //Network DataSource
    IlpDefaultDataSource networkDataSource = 
      getDataSourceProvider().getNetworkDataSource();
    dataStructures.getDataSources().setNetworkDataSource(networkDataSource);

    //Inventory DataSource
    IlpDefaultDataSource inventoryDataSource = 
      getDataSourceProvider().getInventoryDataSource();
    dataStructures.getDataSources().setInventoryDataSource(inventoryDataSource);

    //Services DataSource
    IlpDefaultDataSource servicesDataSource = 
      getDataSourceProvider().getServicesDataSource();
    dataStructures.getDataSources().setServicesDataSource(servicesDataSource);

    //Services Network DataSource
    IlpDefaultDataSource servicesNetworkdataSource = 
      getDataSourceProvider().getServicesNetworkDataSource();
    dataStructures.getDataSources().setServicesNetworkDataSource(servicesNetworkdataSource);

    //Alarms DataSource
    IlpDefaultDataSource alarmsDataSource = 
      WebUtils.lookupDataSource(WebUtils.ALARMS_DATASOURCE_IDENTIFIER);
    dataStructures.getDataSources().setAlarmsDataSource(alarmsDataSource);

    //Service DataSource
    IlpDefaultDataSource serviceDataSource = 
      WebUtils.lookupDataSource(WebUtils.SERVICE_DATASOURCE_IDENTIFIER);
    dataStructures.getDataSources().setServiceDataSource(serviceDataSource);

    ////////////////////////////////////////////////////////////////////////////
    //DataModels
    ////////////////////////////////////////////////////////////////////////////

    //////Table Models

    //Network 
    IntegratedDataModelProvider dataModelProvider = 
      dataStructures.getDataModels().getNetworkObjectsTableModelProvider();
    dataModelProvider.setDataSource(networkDataSource);

    //Equipment 
    dataModelProvider = 
      dataStructures.getDataModels().getInventoryObjectsTableModelProvider();
    dataModelProvider.setDataSource(inventoryDataSource);

    //Service 
    dataModelProvider = 
      dataStructures.getDataModels().getServiceObjectsTableModelProvider();
    dataModelProvider.setDataSource(servicesDataSource);

    //Alarm 
    dataModelProvider = 
      dataStructures.getDataModels().getAlarmsTableModelProvider();
    dataModelProvider.setDataSource(alarmsDataSource);

    //////Tree Models

    //Network 
    dataModelProvider = 
      dataStructures.getDataModels().getNetworkTreeModelProvider();
    dataModelProvider.setDataSource(networkDataSource);

    //Inventory 
    dataModelProvider = 
      dataStructures.getDataModels().getInventoryTreeModelProvider();
    dataModelProvider.setDataSource(inventoryDataSource);

    //Service 
    dataModelProvider = 
      dataStructures.getDataModels().getServiceTreeModelProvider();
    dataModelProvider.setDataSource(servicesDataSource);
  }

  /**
   * Initializes the controls used by the sample. Depends on the action providers 
   * being already initialized. 
   */
  protected void initializeControls() {

    controls.initialize(this);

    //////////////////////////////////////////////////////////////////////////
    //Network Controls
    //////////////////////////////////////////////////////////////////////////

    //Set the label for the network tab 
    getActionProviders().getNetworkActions().updateNetworkNetworkDisplayLabel();

    //Show home on network network at start up
    getActionProviders().getNetworkActions().showHomeOnNetworkNetwork();

    ////////////////////////////////////////////////////////////////////////////
    //Table Controls
    ////////////////////////////////////////////////////////////////////////////

    //Update the datasource on the filters of the tables
    //Network Table
    final IntegratedDataModelProvider networkModelProvider = 
      getDataStructures().getDataModels().getNetworkObjectsTableModelProvider();
    IlpDataSource dataSource = getDataStructures().getDataSources().getNetworkDataSource();
    OriginFilter filter = (OriginFilter) networkModelProvider.getFilter();
    filter.setDataSource(dataSource);
    filter.setMode(OriginFilter.SHOW_ONLY_DIRECT_CHILDREN_MODE);

    //Inventory Table
    final IntegratedDataModelProvider inventoryModelProvider = 
      getDataStructures().getDataModels().getInventoryObjectsTableModelProvider();
    dataSource = getDataStructures().getDataSources().getInventoryDataSource();
    filter = (OriginFilter) inventoryModelProvider.getFilter();
    filter.setDataSource(dataSource);
    filter.setMode(OriginFilter.SHOW_ALL_CHILDREN_MODE);

    //Service Table
    final IntegratedDataModelProvider serviceModelProvider = 
      getDataStructures().getDataModels().getServiceObjectsTableModelProvider();
    dataSource = getDataStructures().getDataSources().getServicesDataSource();
    filter = (OriginFilter) serviceModelProvider.getFilter();
    filter.setDataSource(dataSource);
    filter.setMode(OriginFilter.SHOW_ALL_MODE);

    //Here we refresh the model on a seperate thread so that we do not delay the 
    //response. This improves the user experience. 

    WebUtils.executeAsynchronously(new Runnable() {
      public void run() {
        networkModelProvider.refreshModel();            
      }
      public String toString() {
        return "Refresh Network Model";
      }
    });

    WebUtils.executeAsynchronously(new Runnable() {
      public void run() {
        inventoryModelProvider.refreshModel();            
      }
      public String toString() {
        return "Refresh Inventory Model";
      }
    });

    WebUtils.executeAsynchronously(new Runnable() {
      public void run() {
        serviceModelProvider.refreshModel();            
      }
      public String toString() {
        return "Refresh Service Model";
      }
    });
  }

  /**
   * Initializes the action providers used by the sample. 
   */
  protected void initializeActionProviders() {
    actionProviders.initialize(this);
  }

  /**
   * Should create sample alarms to show at application startup.
   * <p/>
   * By default does not create any alarms.
   */
  protected void createSampleAlarms() {
  }
  
  //////////////////////////////////////////////////////////////////////////////
  //Accessors and Modifiers
  //////////////////////////////////////////////////////////////////////////////

  public SampleDataStructures getDataStructures() {
    return dataStructures;
  }
  public SampleActionProviders getActionProviders() {
    return actionProviders;
  }
  public SampleControls getControls() {
    return controls;
  }
  public DataSourceProvider getDataSourceProvider() {
    return dataSourceProvider;
  }
  public IltFacesDefaultContext getTgoContext() {
    return tgoContext;
  }
  public ServicesUtils getServicesUtils() {
    return servicesUtils;
  }
  public AbstractIntegrationProvider getIntegrationProvider() {
    return integrationProvider;
  }
  public AbstractAlarmGenerator getAlarmGenerator() {
    return alarmGenerator;
  }

  /**
   * This modifier is private because changing this property
   * entails re-creating the strutuctures of the sample.
   */
  private void setDataSourceProvider(DataSourceProvider dataSourceProvider) {
    this.dataSourceProvider = dataSourceProvider;
  }

  //////////////////////////////////////////////////////////////////////////////
  //The following modifiers are private because they are only to be used 
  //internally by the AbstractSampleContext.
  //////////////////////////////////////////////////////////////////////////////

  private void setTgoContext(IltFacesDefaultContext tgoContext) {
    this.tgoContext = tgoContext;
  }
  private void setServicesUtils(ServicesUtils servicesUtils) {
    this.servicesUtils = servicesUtils;
  }
  private void setIntegrationProvider(AbstractIntegrationProvider integrationProvider) {
    this.integrationProvider = integrationProvider;
  }
}