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

import ilog.cpl.IlpNetwork;
import ilog.cpl.network.IlpDefaultNetworkSelectionModel;
import ilog.cpl.network.IlpGeographicPositionConverter;
import ilog.cpl.network.NetworkSelectionEvent;
import ilog.cpl.network.NetworkSelectionListener;
import ilog.cpl.service.IlpContext;
import ilog.tgo.faces.network.dhtml.component.IltFacesDHTMLNetworkView;
import ilog.views.IlvRect;
import ilog.views.faces.dhtml.component.IlvFacesDHTMLOverview;
import ilog.views.maps.format.IlvMapDataPathManager;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformation;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

import monitoring.shared.LoggingUtils;
import monitoring.shared.drilldown.AbstractDrillDownManager;
import monitoring.shared.drilldown.NetworkDrillDownManager;
import monitoring.web.AbstractSampleContext;
import monitoring.web.SampleConstants;
import monitoring.web.controls.popup.NetworkContextualMenuFactory;
import monitoring.web.controls.popup.ServiceContextualMenuFactory;
import monitoring.web.utils.MapsPathResolver;
import monitoring.web.utils.WebUtils;

import org.apache.myfaces.trinidad.component.core.layout.CorePanelGroupLayout;
import org.apache.myfaces.trinidad.component.core.nav.CoreNavigationPane;

import shared.ResourceUtils;


/**
 * Contains all the network related controls in the sample. 
 */
public class NetworkControls {
  
  private static final Map<String,IlvRect> visibleAreas = new HashMap<String,IlvRect>(); 
  public static final String NETWORK_ROOT_VISIBLE_AREA = "NETWORK_ROOT"; 
  public static final String SERVICE_DETAIL_ROOT_VISIBLE_AREA = "SERVICE_DETAIL_ROOT"; 
  
  static {
    
    //This only needs to be done once at the static level
    //Ensure that we can find all maps under the resources/backgrounds directory
    try {
      ServletContext servletContext = WebUtils.getServletContext();
      String backgroundsDirectory = servletContext.getRealPath("/resources/backgrounds");
      IlvMapDataPathManager.AddDataPathResolver(new MapsPathResolver(backgroundsDirectory));
    } catch (Exception e) {
      LoggingUtils.getSampleLogger().log(
          Level.SEVERE,
          "Could not properly backgrounds with this exception: "
          + e.getLocalizedMessage());
      e.printStackTrace();
    }
    
    //Setup the visible areas
    
    //Network Module
    visibleAreas.put(NETWORK_ROOT_VISIBLE_AREA, new IlvRect(-11f,-54.1f,26.4f,16.5f));
    //Portugal
    visibleAreas.put("NetA", new IlvRect(-11.09f, -42.35f, 6.07f, 3.79f)); 
    visibleAreas.put("NetA_NS1", new IlvRect(-10.03f, -43.06f, 4.96f, 3.10f)); 
    //France
    visibleAreas.put("NetB", new IlvRect(-2.04f, -50.19f, 12.4f, 7.75f)); 
    visibleAreas.put("NetB_NET2", new IlvRect(4.23f, -44.92f, 4.96f, 3.10f)); 
    visibleAreas.put("NetB_NS2", new IlvRect(0.53f, -51.01f, 4.96f, 3.10f));
    //UK
    visibleAreas.put("NetC", new IlvRect(-6.82f, -55.81f, 11.16f, 6.97f));
    
    //Service Module
    visibleAreas.put(SERVICE_DETAIL_ROOT_VISIBLE_AREA, new IlvRect(-14.77f, -59.80f, 33.06f, 24.80f));  
    visibleAreas.put("Service1", new IlvRect(-1.79f, -49.50f, 9.60f, 7.20f));
    visibleAreas.put("Service2", new IlvRect(-1.62f, -50.54f, 10.49f, 7.87f));
    visibleAreas.put("Service3", new IlvRect(6.74f, -53.60f, 8.26f, 6.20f));
    visibleAreas.put("Service4", new IlvRect(-8.45f, -43.34f, 9.24f, 6.93f));
    visibleAreas.put("Service5", new IlvRect(-9.93f, -42.04f, 4.96f, 3.72f));
    visibleAreas.put("Service6", new IlvRect(-10.49f, -44.27f, 9.92f, 7.44f));
    visibleAreas.put("Service7", new IlvRect(-11.09f, -48.44f, 16.53f, 12.40f));
    visibleAreas.put("Service8", new IlvRect(-11.23f, -42.63f, 6.2f, 4.65f));
    visibleAreas.put("Service9", new IlvRect(-11.40f, -41.13f, 4.96f, 3.72f));
    visibleAreas.put("Service10", new IlvRect(-11.68f, -41.44f, 4.96f, 3.72f));    
  }
  
  //Network related controls
  private IltFacesDHTMLNetworkView networkNetworkView;
  private IltFacesDHTMLNetworkView serviceNetworkView;
  private IltFacesDHTMLNetworkView serviceDetailNetworkView;

  private IlvFacesDHTMLOverview serviceOverview;
  private IlvFacesDHTMLOverview networkOverview;

  private CorePanelGroupLayout serviceOverviewPanel;
  private CorePanelGroupLayout networkOverviewPanel;

  private CoreNavigationPane tabs;

  private String networkNetworkDisplayLabel;
  private String networkServiceDisplayLabel;
  private String serviceDetailNetworkWindowTitle;

  private NetworkDrillDownManager networkDrillDownManager;

  private NetworkContextualMenuFactory networkContextualMenuFactory;
  private ServiceContextualMenuFactory serviceContextualMenuFactory;

  private NetworkDisplayLabelUpdater networkDisplayLabelUpdater;
  private ServiceDisplayLabelUpdater serviceDisplayLabelUpdater;
  
  private IlvRect serviceDetailNetworkDisplayedArea;
  private IlvRect networkNetworkDisplayedArea;
  
  private static int DEFAULT_SERVICES_DETAIL_VIEW_WIDTH = 450;
  private static int DEFAULT_SERVICES_DETAIL_VIEW_HEIGHT = 200;

  private int serviceDetailViewWidth;
  private int serviceDetailViewHeight;
  
  private boolean networkNetworkVisibleAreaChanged = false;
  
  private NetworkViewSelectionPropagator networkViewSelectionPropagator;
  
  /**
   * Creates all the network related controls. 
   */
  public NetworkControls() {

    FacesContext facesContext = FacesContext.getCurrentInstance();
    
    //Create the network network view
    networkNetworkView = (IltFacesDHTMLNetworkView) facesContext
    .getApplication().createComponent(
        IltFacesDHTMLNetworkView.getComponentType());

    //This is so because at the time when the view is being created
    //in this sample, the ID of the created JSF component is not yet in synch
    //with the JSP page, so we need to specify it, to ensure that the framework
    //can properly find its components
    networkNetworkView.setId(SampleConstants.NETWORK_NETWORK_COMPONENT_ID);

    //Create the service network view
    serviceNetworkView = (IltFacesDHTMLNetworkView) facesContext
    .getApplication().createComponent(IltFacesDHTMLNetworkView.getComponentType());

    //Same as network network view
    serviceNetworkView.setId(SampleConstants.SERVICE_NETWORK_COMPONENT_ID);

    //Create the network network view
    serviceDetailNetworkView = (IltFacesDHTMLNetworkView) facesContext
    .getApplication().createComponent(IltFacesDHTMLNetworkView.getComponentType());

    //Same as network network view
    serviceDetailNetworkView.setId(SampleConstants.SERVICE_DETAIL_NETWORK_COMPONENT_ID);

    //Create the selection propagators
    networkViewSelectionPropagator = new NetworkViewSelectionPropagator();

    //Create the network drill down manager
    networkDrillDownManager = new NetworkDrillDownManager();

    //Create contextual menus
    serviceContextualMenuFactory = new ServiceContextualMenuFactory();
    createNetworkContextualMenu();
    
    //Create the label updater
    networkDisplayLabelUpdater = new NetworkDisplayLabelUpdater();
    serviceDisplayLabelUpdater = new ServiceDisplayLabelUpdater();
  }

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

  /**
   * Initializes the network related controls.
   */
  public void initialize(AbstractSampleContext sampleContext) {

    //1- Update the components with the proper TGO context
    IlpContext context = sampleContext.getTgoContext();

    //This is needed as the settings from the JSPf tag have not yet been applied
    //to this faces component
    networkNetworkView.setContext(context);
    serviceNetworkView.setContext(context);
    serviceDetailNetworkView.setContext(context);

    //2- Apply default stylesheets
    networkNetworkView.setStyleSheets(SampleConstants.NETWORK_NETWORK_STYLESHEETS);
    serviceNetworkView.setStyleSheets(SampleConstants.SERVICE_NETWORK_STYLESHEETS);
    serviceDetailNetworkView.setStyleSheets(SampleConstants.SERVICE_DETAIL_NETWORK_STYLESHEETS);

    //3- Configure the selection propagator for the network network
    networkViewSelectionPropagator.setApplicationContext(sampleContext);

    //Find the Network network
    IlpNetwork networkNetwork = null;
    IlpNetwork serviceNetwork = null;
    IlpNetwork serviceDetailNetwork = null;
    try {
      networkNetwork = getNetworkNetworkView().getNetwork();
      serviceNetwork = getServiceNetworkView().getNetwork();
      serviceDetailNetwork = getServiceDetailNetworkView().getNetwork();
    } catch (Exception e) {
      LoggingUtils.getSampleLogger().log(
          Level.SEVERE,
          "Could not find the Network Views with this exception:"
          + e.getLocalizedMessage());
      e.printStackTrace();
    }

    //Install Geographic position converter
    IlvCoordinateTransformation transformation =
      IlvCoordinateTransformation.CreateTransformation(
          IlvGeographicCoordinateSystem.KERNEL, IlvGeographicCoordinateSystem.WGS84);    
    networkNetwork.setPositionConverter(new IlpGeographicPositionConverter(transformation,true));    
    serviceDetailNetwork.setPositionConverter(new IlpGeographicPositionConverter(transformation,true));    

    //Install selection model for the Service network
    serviceNetwork.setSelectionModel(new ServiceViewSelectionModel());
    
    //Install the propagator on the network network 
    networkNetwork.getSelectionModel().addNetworkSelectionListener(
        networkViewSelectionPropagator);
    serviceNetwork.getSelectionModel().addNetworkSelectionListener(
        networkViewSelectionPropagator);

    //4- Configure the network drill down manager
    //Set the component
    networkDrillDownManager.setComponent(networkNetwork);
    //Set the tree datasource (which is shared with the network)
    networkDrillDownManager.setTreeDataSource(sampleContext
        .getDataStructures().getDataSources().getNetworkDataSource());
    
    //This allows the drilldown triggered by the table and tree know
    //when the visible area has changed, and thus re-fit view accordingly
    networkDrillDownManager.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if(evt.getPropertyName() == AbstractDrillDownManager.CURRENT_ROOT) {
          setNetworkNetworkVisibleAreaChanged(true);
        }
      }
    });
    
    //5- Configure contextual menu
    
    //Now initialize them...
    networkContextualMenuFactory.initialize(sampleContext);
    serviceContextualMenuFactory.initialize(sampleContext);

    //Network
    networkContextualMenuFactory
    .setTargetFacesViewIdentifier(SampleConstants.NETWORK_NETWORK_COMPONENT_ID);

    //Service
    serviceContextualMenuFactory
    .setTargetFacesViewIdentifier(SampleConstants.SERVICE_NETWORK_COMPONENT_ID);

    //7- Configure and install listener to udpate the network labels
    networkDisplayLabelUpdater.setApplicationContext(sampleContext);
    serviceDisplayLabelUpdater.setApplicationContext(sampleContext);

    networkDrillDownManager.addPropertyChangeListener(
        AbstractDrillDownManager.CURRENT_ROOT, networkDisplayLabelUpdater);
    
    try {
      serviceNetworkView.getNetwork().getSelectionModel().addNetworkSelectionListener(serviceDisplayLabelUpdater);
    } catch (Exception e) {
      LoggingUtils.getSampleLogger().log(
          Level.WARNING, 
          "Could not configure service display label update mechanism with this excpetion: \n" + e.getLocalizedMessage());
      e.printStackTrace();
    }
    
    setNetworkNetworkDisplayLabel(ResourceUtils.getString("labelNetworkRootLevel"));
    setServiceNetworkDisplayLabel(ResourceUtils.getString("labelServiceRootLevel"));
    
    //8- Specify the default size of the service details view
    serviceDetailViewHeight = DEFAULT_SERVICES_DETAIL_VIEW_HEIGHT;
    serviceDetailViewWidth = DEFAULT_SERVICES_DETAIL_VIEW_WIDTH;    
    
    //9- Set default visible areas
    //Around Europe
    serviceDetailNetworkDisplayedArea = getVisibleAreaForObjectId(SERVICE_DETAIL_ROOT_VISIBLE_AREA);
    networkNetworkDisplayedArea = getVisibleAreaForObjectId(NETWORK_ROOT_VISIBLE_AREA);
  }

  public IlvRect getVisibleAreaForObjectId(Object objectId) {
    return (IlvRect)visibleAreas.get(objectId);
  }
  
  //////////////////////////////////////////////////////////////////////////////
  //Accessors and Modifiers
  //////////////////////////////////////////////////////////////////////////////
  public IltFacesDHTMLNetworkView getNetworkNetworkView() {
    return networkNetworkView;
  }
  public void setNetworkNetworkView(
      IltFacesDHTMLNetworkView networkNetworkView) {
    this.networkNetworkView = networkNetworkView;
  }
  public IltFacesDHTMLNetworkView getServiceNetworkView() {
    return serviceNetworkView;
  }
  public void setServiceNetworkView(
      IltFacesDHTMLNetworkView serviceNetworkView) {
    this.serviceNetworkView = serviceNetworkView;
  }
  public CoreNavigationPane getTabs() {
    return tabs;
  }
  public void setTabs(CoreNavigationPane tabs) {
    this.tabs = tabs;
  }
  public String getNetworkNetworkDisplayLabel() {
    return networkNetworkDisplayLabel;
  }
  public void setNetworkNetworkDisplayLabel(String networkNetworkDisplayLabel) {
    this.networkNetworkDisplayLabel = networkNetworkDisplayLabel;
  }
  public String getServiceNetworkDisplayLabel() {
    return networkServiceDisplayLabel;
  }
  public void setServiceNetworkDisplayLabel(String networkServiceDisplayLabel) {
    this.networkServiceDisplayLabel = networkServiceDisplayLabel;
  }
  public IlvFacesDHTMLOverview getNetworkOverview() {
    return networkOverview;
  }
  public void setNetworkOverview(IlvFacesDHTMLOverview networkOverview) {
    this.networkOverview = networkOverview;
  }
  public IlvFacesDHTMLOverview getServiceOverview() {
    return serviceOverview;
  }
  public void setServiceOverview(IlvFacesDHTMLOverview serviceOverview) {
    this.serviceOverview = serviceOverview;
  }
  public CorePanelGroupLayout getNetworkOverviewPanel() {
    return networkOverviewPanel;
  }
  public void setNetworkOverviewPanel(CorePanelGroupLayout networkOverviewPanel) {
    this.networkOverviewPanel = networkOverviewPanel;
  }
  public CorePanelGroupLayout getServiceOverviewPanel() {
    return serviceOverviewPanel;
  }
  public void setServiceOverviewPanel(CorePanelGroupLayout serviceOverviewPanel) {
    this.serviceOverviewPanel = serviceOverviewPanel;
  }
  public NetworkDrillDownManager getNetworkDrillDownManager() {
    return networkDrillDownManager;
  }

  public NetworkContextualMenuFactory getNetworkContextualMenuFactory() {
    return networkContextualMenuFactory;
  }
  public void setNetworkContextualMenuFactory(
      NetworkContextualMenuFactory networkContextualMenuFactory) {
    this.networkContextualMenuFactory = networkContextualMenuFactory;
  }
  public ServiceContextualMenuFactory getServiceContextualMenuFactory() {
    return serviceContextualMenuFactory;
  }
  public void setServiceContextualMenuFactory(
      ServiceContextualMenuFactory serviceContextualMenuFactory) {
    this.serviceContextualMenuFactory = serviceContextualMenuFactory;
  }

  public IltFacesDHTMLNetworkView getServiceDetailNetworkView() {
    return serviceDetailNetworkView;
  }
  public void setServiceDetailNetworkView(
      IltFacesDHTMLNetworkView serviceDetailNetworkView) {
    this.serviceDetailNetworkView = serviceDetailNetworkView;
  }
  public int getServiceDetailViewHeight() {
    return serviceDetailViewHeight;
  }
  public int getServiceDetailViewWidth() {
    return serviceDetailViewWidth;
  }

  public String getServiceDetailNetworkWindowTitle() {
    return serviceDetailNetworkWindowTitle;
  }
  public void setServiceDetailNetworkWindowTitle(String serviceNetworkWindowTitle) {
    this.serviceDetailNetworkWindowTitle = serviceNetworkWindowTitle;
  }

  public IlvRect getServiceDetailNetworkDisplayedArea() {
    return serviceDetailNetworkDisplayedArea;
  }
  public void setServiceDetailNetworkDisplayedArea(
      IlvRect serviceDetailNetworkDisplayedArea) {
    this.serviceDetailNetworkDisplayedArea = serviceDetailNetworkDisplayedArea;
  }
 
  public IlvRect getNetworkNetworkDisplayedArea() {
    return networkNetworkDisplayedArea;
  }

  public void setNetworkNetworkDisplayedArea(IlvRect networkNetworkDisplayedArea) {
    this.networkNetworkDisplayedArea = networkNetworkDisplayedArea;
  }

  public boolean getNetworkNetworkVisibleAreaChanged() {
    return networkNetworkVisibleAreaChanged;
  }

  public void setNetworkNetworkVisibleAreaChanged(
      boolean networkNetworkVisibleAreadChanged) {
    this.networkNetworkVisibleAreaChanged = networkNetworkVisibleAreadChanged;
  }

  //////////////////////////////////////////////////////////////////////////////
  //Private Methods
  //////////////////////////////////////////////////////////////////////////////
  /**
   * This contextual menu factory needs special care as different implementations
   * need to be used depending on the underlying OSS. So we read it from a 
   * settings file.
   */
  private void createNetworkContextualMenu() {
    Exception x = null;
    try {
      String menuClassName = 
        WebUtils.getSampleSetting(WebUtils.NETWORK_NETWORK_CONTEXTUAL_MENU_CLASS_NAME_SETTING);
      
      if(menuClassName != null) {

        Class<?> menuClass = Class.forName(menuClassName);

        Object menuInstance = menuClass.newInstance();

        if(menuInstance instanceof NetworkContextualMenuFactory) {
          networkContextualMenuFactory = (NetworkContextualMenuFactory)menuInstance; 
        }
      }
    } catch (Exception e) {
      //Handled below...
      x = e;
    }

    if(networkContextualMenuFactory == null) {
      LoggingUtils.getSampleLogger().log(
          Level.SEVERE, 
          "Could not create contextual menu for network module. Check if the value of the property \" {0} \" is properly set in the \" {1} \" properties file",
          new Object [] {WebUtils.NETWORK_NETWORK_CONTEXTUAL_MENU_CLASS_NAME_SETTING, 
              WebUtils.SAMPLE_SETTINGS_RESOURCE_BUNDLE_NAME});
      if (null != x) {
        x.printStackTrace();
      }
    }
  }

  //////////////////////////////////////////////////////////////////////////////
  //Internal Types
  //////////////////////////////////////////////////////////////////////////////
  /**
   * Custom selection model for the Service network, it does not fire
   * notifications when adding a Collection of objects. This is necessary
   * to prevent that these notifications reach the Trinidad tree.
   */
  static class ServiceViewSelectionModel extends IlpDefaultNetworkSelectionModel {
    private boolean disableNotification = false;
    /**
     * @Override
     */
    public void fireNetworkSelectionEvent(NetworkSelectionEvent event) {
      if (!disableNotification) {
        super.fireGraphSelectionEvent(event);
      }
    }
    /**
     * @Override
     */
    public void setSelectedObjects(Collection objects) {
      disableNotification = true;
      try {
        super.setSelectedObjects(objects);
      } finally {
        disableNotification = false;
      }
    }
    /**
     * @Override
     */
    public void setSelectedRepresentationObjects(Collection selected) {
      disableNotification = true;
      try {
        super.setSelectedRepresentationObjects(selected);
      } finally {
        disableNotification = false;
      }
    }
  }

  /**
   * Handles the propagation of selections in the network view to the 
   * other main components (tree and table).
   */
  static class NetworkViewSelectionPropagator implements NetworkSelectionListener {
    private AbstractSampleContext sampleContext;

    public AbstractSampleContext getSampleContext() {
      return sampleContext;
    }
    public void setApplicationContext(AbstractSampleContext sampleContext) {
      this.sampleContext = sampleContext;
    }
    public void objectsAdded(NetworkSelectionEvent event) {
      if(WebUtils.isApplicationLive()) {
        sampleContext
        .getActionProviders()
        .getSharedActions()
        .handleSelectionObjectAddedOnCurrentView(event.getObjects());
      } 
    }

    public void objectsRemoved(NetworkSelectionEvent event) {
      if(WebUtils.isApplicationLive()) {
        sampleContext.getActionProviders().getSharedActions()
        .handleSelectionObjectRemovedOnCurrentView(
            event.getNetworkSelectionModel());
      } 
    }
  }

  /**
   * Ensures that the label of network module's network display label.
   */
  class NetworkDisplayLabelUpdater implements PropertyChangeListener {
    private AbstractSampleContext sampleContext;

    public AbstractSampleContext getSampleContext() {
      return sampleContext;
    }
    public void setApplicationContext(AbstractSampleContext sampleContext) {
      this.sampleContext = sampleContext;
    }
    public void propertyChange(PropertyChangeEvent evt) {
      sampleContext.getActionProviders().getNetworkActions().updateNetworkNetworkDisplayLabel();
    }
  }

  /**
   * Ensures that the label of service module's network display label.
   */
  class ServiceDisplayLabelUpdater implements NetworkSelectionListener {
    private AbstractSampleContext sampleContext;

    public AbstractSampleContext getSampleContext() {
      return sampleContext;
    }
    public void setApplicationContext(AbstractSampleContext sampleContext) {
      this.sampleContext = sampleContext;
    }
    public void objectsAdded(NetworkSelectionEvent arg0) {
      if(WebUtils.isApplicationLive()) {
        sampleContext.getActionProviders().getNetworkActions().updateNetworkServiceDisplayLabel();
      }       
    }
    public void objectsRemoved(NetworkSelectionEvent arg0) {
      if(WebUtils.isApplicationLive()) {
        sampleContext.getActionProviders().getNetworkActions().updateNetworkServiceDisplayLabel();
      }  
    }
  }
}