/*
 * 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.model.integration.filters;

import ilog.cpl.datasource.IlpDataSource;
import ilog.cpl.datasource.structure.IlpContainer;
import ilog.cpl.model.IlpObject;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Filter that potentially filters out objects according to its modes. If 
 * no origin is specified it shows the roots. Note that if SHOW_ALL_MODE is set 
 * all objects are shown even if the origin is null.  
 */
public class OriginFilter extends TypeFilter {

  public static int SHOW_ONLY_DIRECT_CHILDREN_MODE = 0;
  public static int SHOW_ALL_CHILDREN_MODE = 1;
  public static int SHOW_ALL_MODE = 2;
  public static int SHOW_NOTHING_MODE = 3;

  private IlpObject origin;
  private IlpDataSource dataSource;
  private int mode;

  //Given that the structure of the objects in the datasources of this sample 
  //does not change, we can cache the roots any give data source. This save us 
  //some good computation time.   
  private Map<IlpDataSource,List<IlpObject>> dataSourceToRoots = new HashMap<IlpDataSource,List<IlpObject>>();

  public boolean accept(Object object) {

    boolean accept = false;

    if (getDataSource() != null) {

      List<IlpObject> eligibleObjects = getEligibleObjects(object);

      //If it is eligible     
      if (eligibleObjects.contains(object)) {

        //If we can test for type safely
        if (object instanceof IlpObject && getType() != null) {
          accept = getType().isAssignableFrom(
              ((IlpObject) object).getIlpClass());
        }
      }
    }
    return accept;
  }

  private List<IlpObject> getEligibleObjects(Object object) {

    List<IlpObject> eligibleObjects = new ArrayList<IlpObject>();

    if(getMode() == SHOW_NOTHING_MODE) {
      return eligibleObjects;
    }
    
    IlpObject objOrigin = getOrigin();

    if (objOrigin == null) {

      //If show all we show all 
      if (getMode() == SHOW_ALL_MODE) {
        eligibleObjects.addAll(dataSource.getObjects());
      }
      //Show only the roots
      else {
        eligibleObjects.addAll(getRoots());
      }
    } else if (getMode() == SHOW_ALL_MODE) {
      eligibleObjects.addAll(dataSource.getObjects());
    } else if (objOrigin != null) {
      IlpContainer container = getDataSource().getContainerInterface(origin);
      if (container != null) {
        if (getMode() == SHOW_ONLY_DIRECT_CHILDREN_MODE) {
          eligibleObjects.addAll(getObjectsForIds(container.getChildren(origin)));
        } else if (getMode() == SHOW_ALL_CHILDREN_MODE) {
          eligibleObjects.addAll(getAllChildren(origin, container));
        }
      }
      if (objOrigin.equals(object)) {
        eligibleObjects.add(objOrigin);
      }
    }
    return eligibleObjects;
  }

  private List<IlpObject> getRoots() {

    //Check in our cache
    List<IlpObject> roots = dataSourceToRoots.get(getDataSource());

    //This is the first time around and we need to compute it
    if (roots == null) {
      roots = new ArrayList<IlpObject>();

      Collection<IlpObject> allObjects = getDataSource().getObjects();

      for (IlpObject object : allObjects) {
        if (getDataSource().getChildInterface(object) == null) {
          roots.add(object);
        }
      }
      dataSourceToRoots.put(getDataSource(), roots);
    } 

    return roots;
  }

  private List<IlpObject> getAllChildren(IlpObject object, IlpContainer container) {

    List<IlpObject> allChildren = new ArrayList<IlpObject>();

    List<IlpObject> directChildren = getObjectsForIds(container.getChildren(object));

    allChildren.addAll(directChildren);
    for (IlpObject child : directChildren) {
      IlpContainer childContainer = getDataSource().getContainerInterface(child);
      if (childContainer != null) {
        allChildren.addAll(getAllChildren(child, childContainer));
      }
    }
    return allChildren;
  }

  private List<IlpObject> getObjectsForIds(Collection<Object> ids) {
    List<IlpObject> children = new ArrayList<IlpObject>(ids.size());
    for (Object childId : ids) {
      children.add(dataSource.getObject(childId));
    }
    return children;
  }

  public IlpObject getOrigin() {
    return origin;
  }
  public void setOrigin(IlpObject origin) {
    this.origin = origin;
  }
  public IlpDataSource getDataSource() {
    return dataSource;
  }
  public void setDataSource(IlpDataSource dataSource) {

    //Clear references to our roots cache 
    dataSourceToRoots.remove(this.dataSource);

    //Update our datasource now 
    this.dataSource = dataSource;
  }
  public int getMode() {
    return mode;
  }
  public void setMode(int mode) {
    this.mode = mode;
  }
}