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

import ilog.cpl.IlpNetwork;
import ilog.cpl.datasource.IlpAbstractHierarchyAdapter;
import ilog.cpl.datasource.IlpDataSource;
import ilog.cpl.datasource.structure.IlpChild;
import ilog.cpl.model.IlpAttributeValueHolder;
import ilog.cpl.model.IlpClass;
import ilog.cpl.model.IlpObject;
import ilog.views.util.IlvResourceUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import monitoring.shared.CommonUtils;
import monitoring.shared.LoggingUtils;


/**
 * Implementation of the "drill-down" mechanism for an <code>IlpNetwork</code>.
 */
public class NetworkDrillDownManager extends AbstractDrillDownManager {

  /**
   * <code>IlpNetwork</code> implementation.
   * 
   * @see monitoring.shared.drilldown.AbstractDrillDownManager#getAdapter()
   */
  protected IlpAbstractHierarchyAdapter getAdapter() {
    return getNetwork().getAdapter();
  }

  /**
   * <code>IlpNetwork</code> implementation.
   * 
   * @see monitoring.shared.drilldown.AbstractDrillDownManager#ensureComponentIsVisible()
   */
  protected void ensureComponentIsVisible() {
    getNetwork().getView().ensureVisible();
  }

  /**
   * <code>IlpNetwork</code> implementation.
   * 
   * @see monitoring.shared.drilldown.AbstractDrillDownManager#ensureObjectIsVisibleInComponent(ilog.cpl.model.IlpObject)
   */
  protected void ensureObjectIsVisibleInComponent(IlpObject object) {
    getNetwork().ensureVisible(object);
  }

  /**
   * <code>IlpNetwork</code> implementation.
   * 
   * @see monitoring.shared.drilldown.AbstractDrillDownManager#selectObjectInComponent(ilog.cpl.model.IlpObject)
   */
  protected void selectObjectInComponent(IlpObject object) {
    getNetwork().getSelectionModel().setSelectedObject(object);
  }

  /**
   * <code>IlpNetwork</code> implementation. 
   * 
   * @see monitoring.shared.drilldown.AbstractDrillDownManager#showDetails(ilog.cpl.model.IlpObject)
   */
  protected void showDetails(IlpObject obj) {
    Logger log = LoggingUtils.getSampleLogger();
    if (obj == null) {
      String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"showNetworkDetailsForNullObject");
      log.log(Level.FINE,msg);
      resetOrigins();
      return;
    }
    String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"showNetworkDetailsForObject");
    log.log(Level.FINE,
        msg,
        new Object[]{obj.getIdentifier(), obj.getClass().getName()});
    if (hasSubNetwork(obj)) {

      // if the object has a sub-network, show it
      showSubNetwork(obj);

      ensureComponentIsVisible();
    } else {
      // If there is no sub-network, show the
      // network to which this object belongs
      showParentNetwork(obj);
    }
  }

  /** 
   * Sets the provided <code>IlpNetwork</code> as the underlying graphic component.
   */
  public void setComponent(IlpNetwork network) {
    super.setComponent(network);
  }

  /**
   * Returns the underlying <code>IlpNetwork</code>. 
   */
  private IlpNetwork getNetwork() {
    return (IlpNetwork) component;
  }

  /**
   * Show the network roots.
   */
  public void showRootNetwork() {
    Logger log = LoggingUtils.getSampleLogger();
    if (getCurrentRoot() != null) {
      resetOrigins();
      String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"ResetNetworkOrigins");
      log.log(Level.FINER, msg);
      setCurrentRoot(null);
    } else{
      String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"AlreadyShowingRootNetwork");
      log.log(Level.FINER,msg);
    }
  }

  /**
   * Show the network rooted at the provided object.
   */
  protected void showSubNetwork(IlpObject obj) {
    Logger log = LoggingUtils.getSampleLogger();
    if (getCurrentRoot() != obj) {

      List<Object> originList = new ArrayList<Object>();
      originList.add(obj.getIdentifier());

      if (!originList.equals(getOrigins())) {

        // Second parameter is false, as we do not want to show the
        // origins themselves, only their children
        setOrigins(originList, false);
        String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"SetNetworkComponentOrigin");
        log.log(Level.FINER, msg, obj.getIdentifier());

        setCurrentRoot(obj);
      }
    } else{
      String msg=IlvResourceUtil.getServerLocaleString(NetworkDrillDownManager.class,"showingSubNetwork");
      log.log(Level.FINER,msg, obj.getIdentifier());
    }
  }

  /**
   * Shows the network that encloses the provided object.
   */
  public void showParentNetwork(IlpObject obj) {
    IlpObject detailObj = null;
    IlpDataSource dataSource = getTreeDataSource();

    if (isRootObject(obj)) {

      // if the object is a root, show the root network
      resetOrigins();
      detailObj = obj;
    } else {
      IlpChild childItf = dataSource.getChildInterface(obj);
      IlpObject parent = dataSource.getObject(childItf.getParent(obj));

      // Check if the parent has a subnetwork
      // If this object is an equipment detail, its parent will
      // not have a network, so we much check further up the tree
      if (hasSubNetwork(parent)) {
        showSubNetwork(parent);
        detailObj = obj;
      } else {
        // Show the next higher level of parent network
        showParentNetwork(parent);
      }
    }

    if (detailObj != null) {
      // Select the child object, and make sure it is visble
      ensureObjectIsVisibleInComponent(detailObj);
      selectObjectInComponent(detailObj);
    }
  }

  /**
   * Checks if the provided object is the root of subnetwork.
   */
  public boolean hasSubNetwork(IlpObject obj) {
    return canDrillDown(obj);
  }

  /**
   * Check if we can drill down on the given object.
   * Objects that allow drill down have an associated configuration file
   */
  public boolean canDrillDown(IlpObject ilpObj) {

    // Check if object itself has a css config file: normally groups/sub-networks
    if (canDrillDownGroup(ilpObj))
      return true;

    // Check if object 'type' has a css config file: normally network elements
    return (canDrillDownNE(ilpObj));
  }

  /**
   * Checks if the provided object can be drilled down as a group and potencially 
   * exposed a subnetwork.
   */
  public boolean canDrillDownGroup(IlpObject ilpObj) {

    // Check if object itself has a css config file: normally groups/sub-networks
    String configFileName = 
      CommonUtils.computeConfigurationFileNameForNetworkElement(ilpObj);

    return 
    (getComponent().getContext().getURLAccessService().getFileLocation(configFileName) != null);
  }

  /**
   * Checks if the provided object can be drilled down as a network element and potencially 
   * exposed equipment details.
   */
  public boolean canDrillDownNE(IlpObject ilpObj) {

    // Check if object vendor/type has a css config file: normally network elements
    IlpClass monitoringNEClass = 
      CommonUtils.getMonitoringNetworkElementClass(getContext());
    if ((monitoringNEClass != null) && 
        (monitoringNEClass.isAssignableFrom(ilpObj.getIlpClass()))) {

      String neConfigFileName = 
        CommonUtils.computeConfigurationFileNameForSubnetwork(ilpObj);

      return 
      (getContext().getURLAccessService().getFileLocation(neConfigFileName) != null);
    }
    return false;
  }

  /**
   * Checks if the provided object has services associcated with it.
   */
  public boolean canShowService(IlpObject ilpObj) {

    // get service names
    String[] servicesStrArr = getServiceNames(ilpObj);

    if (servicesStrArr == null || servicesStrArr.length == 0) {
      return false;
    }
    return true;
  }

  /**
   * Returns the names of the services associated with the provided object.
   */
  public String[] getServiceNames(IlpObject ilpObj) {

    String servicesStr = null;
    String[] servicesStrArr = null;

    // get 'services' attribute value of the selected NE
    Object services = ilpObj.getAttributeValue("services");
    if ((services != null) && 
        (services != IlpAttributeValueHolder.VALUE_NOT_SET)) {
      servicesStr = (String) services;
      servicesStrArr = servicesStr.split(",");
    }

    return servicesStrArr;
  }
}