/*
 * 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.action;

import ilog.cpl.graph.IlpGraphSelectionModel;
import ilog.cpl.model.IlpObject;
import ilog.cpl.model.IlpRepresentationObject;
import ilog.cpl.util.IlpFilter;
import ilog.cpl.util.selection.IlpObjectSelectionModel;
import ilog.tgo.model.IltAlarm;

import java.util.Collection;
import java.util.List;
import java.util.logging.Level;

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;

import monitoring.generator.AbstractAlarmGenerator;
import monitoring.shared.LoggingUtils;
import monitoring.web.AbstractSampleContext;
import monitoring.web.controls.SharedControls;
import monitoring.web.model.integration.IntegratedDataModelProvider;
import monitoring.web.model.integration.filters.OriginFilter;
import monitoring.web.utils.Lock;
import monitoring.web.utils.WebUtils;

import org.apache.myfaces.trinidad.component.core.CorePoll;
import org.apache.myfaces.trinidad.component.core.nav.CoreCommandNavigationItem;


/**
 * Action provider that holds actions that are shared across the sample.
 */
public class SharedActionProvider extends BaseActionProvider {

  /**
   * Identifier of the network module.
   */
  public static final int NETWORK_MODULE = 0;
  /**
   * Identifier of the inventory module.
   */
  public static final int INVENTORY_MODULE = 1;
  /**
   * Identifier of the service module.
   */
  public static final int SERVICE_MODULE = 2;

  /**
   * Object currently selected.
   */
  private IlpObject currentlySelectedObject;

  /**
   * Shows and handles the logistics behind showing the network module.
   */
  public String showNetworkModule() {

    //Switch to the appropriate tree tab
    getActionProviders().getTreeActions().switchToNetworkTab();

    //Show appropriate component in main view
    showNetworkNetworkView();

    //Show the appropriate set of tables
    getActionProviders().getTableActions().showNetworkTables();

    return null;
  }

  /**
   * Shows and handles the logisitcs behdind showing the service module.
   */
  public String showServiceModule() {

    //Switch to the appropriate tree tab
    getActionProviders().getTreeActions().switchToServiceTab();

    //Show appropriate component in main view
    showServiceNetworkView();

    //Show the appropriate set of tables
    getActionProviders().getTableActions().showServiceTables();

    return null;
  }

  /**
   * Shows and handles the logisitcs behdind showing the inventory module.
   */
  public String showInventoryModule() {

    //Switch to the appropriate tree tab
    getActionProviders().getTreeActions().switchToInventoryTab();

    //Show appropriate component in main view
    showInventoryEquipmentView();

    //Show the appropriate set of tables
    getActionProviders().getTableActions().showInventoryTables();

    return null;
  }

  /**
   * Entry point method used to trigger the selection of a given object in any given 
   * view. It trigger all the effects caused by the selection of an object. 
   */
  public void setSelectedObject(IlpObject object) {

    Lock waitLock = sampleContext.COMPLETION_LOCK;

    synchronized (waitLock) {
      try {
        //////////////////////////////////////////////////////////////////////////
        //1- Find the module of interaction 
        int currentModule = getActionProviders().getSharedActions().getCurrentModule();
        Object jsfLock = null;
        if (currentModule == SharedActionProvider.NETWORK_MODULE) {
          jsfLock = getControls().getNetworkControls().getNetworkNetworkView().getNetwork().getManagerView().getRegion();
        } else if (currentModule == SharedActionProvider.INVENTORY_MODULE) {
          jsfLock = getControls().getEquipmentControls().getInventoryEquipmentView().getEquipment().getManagerView().getRegion();
        } else if (currentModule == SharedActionProvider.SERVICE_MODULE) {
          jsfLock = getControls().getNetworkControls().getServiceNetworkView().getNetwork().getManagerView().getRegion();
        }
        synchronized(jsfLock) {
  
          //Trigger waiting scheme 
          waitLock.executingTask = true;
          
          if ((currentModule == SharedActionProvider.NETWORK_MODULE) || 
              (currentModule == SharedActionProvider.SERVICE_MODULE)) {
    
            //2- Process drill down mechanism
    
            //This needs to be done first to ensure that the appropriate representation
            //objects are created in the view 
            if (currentModule == SharedActionProvider.NETWORK_MODULE) {
              if (object instanceof IltAlarm) {
                object = getDataStructures().getDataSources().getNetworkDataSource().getObject(object.getAttributeValue("managedObjectInstance"));
              }
              //Engage the drill down mechanism on the network network 
              getActionProviders().getNetworkActions().triggerDrillDownOnNetworkNetwork(object, true);
            } else {
              if (object instanceof IltAlarm) {
                object = getDataStructures().getDataSources().getServiceDataSource().getObject(object.getAttributeValue("managedObjectInstance"));
              }
            }
    
            //3- Select the objects in the view
    
            //In the the network module, the tree and table will be notified of the 
            //selection change via a TGO selection model listener
            getActionProviders().getNetworkActions().selectRelatedObjectsOnCurrentView(object, true, true);
    
            //The service module does not synch the selection made on 
            //the network view with the tree view. So we need to manually trigger the 
            //selection of the node in the tree
            if (currentModule == SharedActionProvider.SERVICE_MODULE) {
    
              TreeActionProvider treeActions = 
                getActionProviders().getTreeActions();
    
              //The service view maintains the expansion state
              treeActions.selectObjectOnCurrentTree(object, false, true);
            }
          } else if (currentModule == SharedActionProvider.INVENTORY_MODULE) {
            if (object instanceof IltAlarm) {
              object = getDataStructures().getDataSources().getInventoryDataSource().getObject(object.getAttributeValue("managedObjectInstance"));
            }

            //2- Process drill down mechanism 
            //This needs to be done first to ensure that the appropriate representation
            //objects are created in the view 
    
            //Engage the drill down mechanis 
            getActionProviders().getEquipmentActions().triggerDrillDownOnInventoryEquipment(object, true);
    
            //3- Select the objects in the view
    
            //In the the inventory module, the tree and table will be notified of the 
            //selection change via a TGO selection model listener
            getActionProviders().getEquipmentActions().selectRelatedObjectsOnInventoryView(object, true);
          }
    
          //2- We need to store a local reference to the currently selected object so 
          //that we can appropriately style selections on the client, as some object types
          //such as service and some equipment may not actually be selected in the 
          //selection model due to the nature of the data being displayed
    
          currentlySelectedObject = object;      
          //////////////////////////////////////////////////////////////////////////
        }
      } catch (Exception e) {
        LoggingUtils.getSampleLogger().log(
            Level.SEVERE,
            "Could not properly set the selected object with this exception:"
            + e.getLocalizedMessage());
      } finally {
        //Shutdown waiting scheme 
        waitLock.executingTask = false;        
      }        
    }
  }

  /**
   * Processes the logistics behind reacting to an object being selected on the 
   * current main view. Which means updating the selection on the corresponding 
   * tree view and the filters associated with corresponding tables. 
   */
  public void handleSelectionObjectAddedOnCurrentView(
      IlpRepresentationObject[] selectedRepresentationObjects) {

    if (selectedRepresentationObjects.length > 0) {

      TreeActionProvider treeActions = getActionProviders().getTreeActions();

      //1- Find the object on the tree that is associated with the first 
      //selected object in the view   
      IlpObject object = 
        getTreeSpecificObject(selectedRepresentationObjects[0].getIlpObject());
      //2- Update the tree with the first object      
      treeActions.selectObjectOnCurrentTree(object, false, true);

      //3- Update inventory table 
      EquipmentActionProvider equipmentActions = getActionProviders().getEquipmentActions();
      equipmentActions.updateInventoryTableOrigin(object);
      
      //4- Make sure to update our reference of the selected object 
      currentlySelectedObject = object;      
    }
  }
  /**
   * Processes the logistics behind reacting to an object being no longer selected on the 
   * current main view. Which means updating the selection on the corresponding 
   * tree view and the filters associated with corresponding tables. 
   */
  public void handleSelectionObjectRemovedOnCurrentView(
      IlpObjectSelectionModel selectionModel) {

    AbstractSampleContext context = WebUtils.getSampleContext();
    
    //This case can take place when the application  
    //is being removed from the session
    if(context == null) {
      return;
    }

    Collection<?> selectedObjects = selectionModel.getSelectedObjects();
    
    TreeActionProvider treeActions = 
      context.getActionProviders().getTreeActions();

    int size = selectedObjects.size();

    if (size > 0) {

      //Update the tree with the last object that was selected that is still selected

      //1- Get the last still selected object
      IlpObject objectToSelectInTree = 
        (IlpObject) selectedObjects.toArray()[size - 1];

      //2- Get the tree specific object
      objectToSelectInTree = getTreeSpecificObject(objectToSelectInTree);

      //3- Update Tree selection
      treeActions.selectObjectOnCurrentTree(objectToSelectInTree, false, true);
    } else {
      treeActions.clearObjectSelectionOnCurrentTree();
    }

    //Make sure to clear our reference of the selected object in case it has 
    //been removed from the selection model
    boolean currentObjectStillSelected = false;
    for (Object obj : selectedObjects) {
      if (obj.equals(currentlySelectedObject)) {
        currentObjectStillSelected = true;
      }
    }
    if(!currentObjectStillSelected) {
      currentlySelectedObject = null;
    }
  }

  /**
   * Returns the identifier of the current module being displayed as defined in this class. 
   */
  public int getCurrentModule() {

    SharedControls sharedControls = getControls().getSharedControls();

    String currentFacet = 
      sharedControls.getMainViewSwitcher().getFacetName();

    //If the user has not yet navigated, the facet name is not yet set
    //So we use the default
    if (currentFacet == null) {
      currentFacet = sharedControls.getMainViewSwitcherDefaultFacet();
    }

    return sharedControls.getMainViewSwitcherFacetNames().indexOf(currentFacet);
  }

/**
 * The auto refresh checbox should only be enabled  in the network module, 
 * because otherwise the  network network view can become not in synch  
 * if auto refresh is disabled in one of the other  two modules
 * 
 */
  public boolean getAutoRefreshCheckboxDisabled() {
   
    //JV-4379
    FacesContext fc = FacesContext.getCurrentInstance();
    UIViewRoot view = fc.getViewRoot();
    CoreCommandNavigationItem item = (CoreCommandNavigationItem) view
        .findComponent("form:navigationPaneSwitch:networkNavigationItem");

    Boolean isSelected = item.isSelected();

    return !isSelected;
  }
  /**
   * Toggles the auto refresh rate of the page on/off.
   */
  public void toggleAutoRefresh(ValueChangeEvent event) {

    CorePoll autoRefreshPoll = getControls().getSharedControls().getAutoRefreshRatePoll();

    if (event.getNewValue() != null) {

      String newValue = event.getNewValue().toString();

      Boolean autoRefreshEnabled = null;
      try {
        autoRefreshEnabled = Boolean.valueOf(newValue);        
      } catch (Exception e) {
        LoggingUtils.getSampleLogger().warning
        ("Could not toggle the page auto refresh with this exception:\n" + e.getLocalizedMessage());
        return;
      }
      
      //Simply toggle it as instructed
      autoRefreshPoll.setRendered(autoRefreshEnabled.booleanValue());
    }
  }
  
  /**
   * Updates the auto refresh rate of the page.
   */
  public void updateAutoRefreshRate(ValueChangeEvent event) {

    CorePoll autoRefreshPoll = getControls().getSharedControls().getAutoRefreshRatePoll();

    if (event.getNewValue() != null) {

      String newValue = event.getNewValue().toString();

      Integer autoRefreshRate = null;
      try {
        autoRefreshRate = Integer.valueOf(newValue);        
      } catch (Exception e) {
        LoggingUtils.getSampleLogger().warning
        ("Could not set auto refresh rate with this exception:\n" + e.getLocalizedMessage());
      }

      if(autoRefreshRate != null) {

        //Nothing to do here
        if (autoRefreshPoll == null) {
          return;
        }

        if (autoRefreshRate.intValue() > 0) {

          //Update the rate
          getControls().getSharedControls().setAutoRefreshRate(autoRefreshRate.intValue());

          //Enable the auto refresh if it was disabled before
          if (!autoRefreshPoll.isRendered()) {
            autoRefreshPoll.setRendered(true);
          }
        }
      }
    } else {

      //Update the rate
      getControls().getSharedControls().setAutoRefreshRate(0);

      //Disable the auto refresh
      autoRefreshPoll.setRendered(false);  
    }
  }

  /**
   * Toggles the alarm generation on/off. 
   */
  public void toggleAlarmGeneration(ValueChangeEvent event) {

    AbstractAlarmGenerator alarmGenerator = getSampleContext().getAlarmGenerator();

    if (event.getNewValue() != null) {

      String newValue = event.getNewValue().toString();

      Boolean alarmGenerationEnabled = null;
      try {
        alarmGenerationEnabled = Boolean.valueOf(newValue);        
      } catch (Exception e) {
        LoggingUtils.getSampleLogger().warning
        ("Could toggle alarm generation with this exception:\n" + e.getLocalizedMessage());
        return;
      }

      if(alarmGenerationEnabled.booleanValue()) {
        //Enable the alarm generation 
        alarmGenerator.startAlarmGeneration();          
      } else {
        //Disable the alarm generation
        alarmGenerator.stopAlarmGeneration();  
      }
    }
  }
  
  /**
   * Updates the alarm generation rate.
   */
  public void updateAlarmGenerationRate(ValueChangeEvent event) {

    AbstractAlarmGenerator alarmGenerator = getSampleContext().getAlarmGenerator();

    if (event.getNewValue() != null) {

      String newValue = event.getNewValue().toString();

      Integer alarmGenerationRate = null;
      try {
        alarmGenerationRate = Integer.valueOf(newValue);        
      } catch (Exception e) {
        LoggingUtils.getSampleLogger().warning
        ("Could not set alarm generation rate with this exception:\n" + e.getLocalizedMessage());
      }

      if(alarmGenerationRate != null) {

        //Convert to milli..
        int newAlarmGenerationRate = alarmGenerationRate.intValue() * 1000;

        int oldAlarmGenerationRate = getControls().getSharedControls().getAlarmGenerationRate();

        //Update the public value
        getControls().getSharedControls().setAlarmGenerationRate(newAlarmGenerationRate);

        //Update the period on the generator 
        alarmGenerator.setPeriod(newAlarmGenerationRate);

        if(oldAlarmGenerationRate <= 0) {

          //Enable alarm generation as it was disabled
          alarmGenerator.startAlarmGeneration();          
        }

      } 
    } else {

      //Update the public value
      getControls().getSharedControls().setAlarmGenerationRate(-1);

      //Disable the alarm generation
      alarmGenerator.stopAlarmGeneration();  
    }
  }

  /**
   * Removes all the alarms. This may mean different things depeneding on 
   * the implementation of the AlarmGenerator being used.
   */
  public void removeAllAlarms() {

    //Simply remove all the alarms
    getSampleContext().getAlarmGenerator().removeAllAlarms();
  }

  public boolean isObjectSelected(IlpObject object) {
    
    //1- Find the module 
    int currentModule = 
      getActionProviders().getSharedActions().getCurrentModule();
    
    boolean isSelected = false;
    
    //2- Check the selection models
    try {

      if (currentModule == SharedActionProvider.NETWORK_MODULE) {
        isSelected = getControls().getNetworkControls().getNetworkNetworkView().getNetwork().getSelectionModel().isObjectSelected(object);

      } else if (currentModule == SharedActionProvider.SERVICE_MODULE) {
        isSelected = getControls().getNetworkControls().getServiceNetworkView().getNetwork().getSelectionModel().isObjectSelected(object);

      } else if (currentModule == SharedActionProvider.INVENTORY_MODULE) {
        isSelected = getControls().getEquipmentControls().getInventoryEquipmentView().getEquipment().getSelectionModel().isObjectSelected(object);      
      }
    } catch (Exception e) {
      LoggingUtils.getSampleLogger().warning
      ("Could not compute style with this exception:\n" + e.getLocalizedMessage());
    }

    //This is used for the cases where objects are not in the selection model
    //Special types like services, vendors... and some equipments when 
    //not being displayed
    if(isSelected == false) {
      
      //Check if we have a currently selected and
      //Only if in INVENTORY or SERVICE MODULE 
      if(currentlySelectedObject != null && NETWORK_MODULE != currentModule) {
        isSelected = object.getIdentifier().equals(currentlySelectedObject.getIdentifier());
      } 
    }
    return isSelected;
  }
  
  /**
   * Internal trick to set the currently selected object.
   * <p/>
   * This enusre that the styling on the tree/table works correctly for objects
   * that are not part of the actual TGO selection model (like vendors, and 
   * placeholder objects) 
   */
  void setCurrentlySelectedObject(IlpObject object) {
    currentlySelectedObject = object;
  }
  
  /**
   * Sets the objects provided in the <code>List</code> as the current selection
   * in the provided <code>IlpGraphSelectionModel</code>.
   * 
   * @param clearCurrentSelection if false the current selection is kept, otherwise
   * it is removed.
   */
  void selectObjects(IlpGraphSelectionModel selectionModel, List<IlpObject> objects,
      boolean clearCurrentSelection) {

    if (clearCurrentSelection) {
      selectionModel.setSelectedObjects(objects);
    } else {
      for (IlpObject obj : objects) {
        selectionModel.removeSelectionObject(obj);
      }
    }
  }

  /**
   * Deselects the object provided in the provided <code>IlpGraphSelectionModel</code>.
   */
  void deselectObject(IlpGraphSelectionModel selectionModel, IlpObject object) {
    selectionModel.removeSelectionObject(object);
  }

  /**
   * Updates the origin in the <code>IntegratedDataModelProvider</code> with the provide
   * one. The model is refreshed.
   */
  void updateTableOrigin(IntegratedDataModelProvider modelProvider, IlpObject origin) {

    IlpFilter filter = modelProvider.getFilter();

    if (filter instanceof OriginFilter) {

      OriginFilter originFilter = ((OriginFilter) filter);

      //Update the origin 
      originFilter.setOrigin(origin);

      //Refresh the model
      modelProvider.refreshModel();      
    }
  }

  /**
   * Shows the view associated with the index provided in the main view are of 
   * the sample. 
   */
  void showViewInMainView(int index) {
    SharedControls sharedControls = getControls().getSharedControls();
    String selectedFacetName = sharedControls.getMainViewSwitcherFacetName(index);
    sharedControls.getMainViewSwitcher().setFacetName(selectedFacetName);
  }

  //////////////////////////////////////////////////////////////////////////////
  //Private Methods
  //////////////////////////////////////////////////////////////////////////////
  private IlpObject getTreeSpecificObject(IlpObject object) {

    IlpObject specificObject = null;

    int currentModule = 
      getActionProviders().getSharedActions().getCurrentModule();
    if ((SharedActionProvider.NETWORK_MODULE == currentModule) || 
        (SharedActionProvider.INVENTORY_MODULE == currentModule)) {
      specificObject = object;
    } else if (SharedActionProvider.SERVICE_MODULE == currentModule) {
      specificObject = getSampleContext().getServicesUtils().getServiceTreeNode(object);
    }

    return specificObject;
  }

  private String showNetworkNetworkView() {
    showViewInMainView(0);
    return null;
  }

  private String showInventoryEquipmentView() {
    showViewInMainView(1);
    return null;
  }

  private String showServiceNetworkView() {
    showViewInMainView(2);

    return null;
  }
}