package commons.graph;
import commons.graph.model.Edges;
import commons.graph.model.GraphModelException;
import commons.graph.model.Node;
import commons.graph.model.Nodes;
import commons.graph.worker.CascadedFadeOut;
import commons.graph.worker.DistanceNormalizer;
import commons.graph.worker.EdgeWorker;
import commons.graph.worker.Inverter;
import commons.graph.worker.MaxDistanceFinder;
import commons.graph.worker.RemoveEdgesWithInvisibleNodes;
import commons.graph.worker.WorkerController;
import basics.utl.SysUtl;
public class ClickGraph
{

private String origin = null;
public Nodes   nodes  = null;
public Edges   edges  = null;

public ClickGraph()
{
   nodes = new Nodes();
   edges = new Edges(nodes);
}

public void clear ()
{
   origin = null;
   edges.clear();
   nodes.clear();
}

/**
 * Define how many elements shall be visible in the depth of the graph. The array contains int
 * values that determine the number of visible elements of one depth.
 * @param cascadedSharpness
 */
public void setFadeOut (int[] cascadedSharpness)
{
   CascadedFadeOut cfo = new CascadedFadeOut(cascadedSharpness);
   work(cfo);
}

/**
 * Recalculates all distances so that all value are between 0..1.
 * @return the factor used to normalize the values.
 */
public double normalizeAndInvert (int type, double offset)
{
   MaxDistanceFinder fmd = new MaxDistanceFinder(type);
   work(fmd);
   double max = fmd.getMax();
   if (max != 0) {
      work(new DistanceNormalizer(type, max));
      work(new Inverter(type, offset));
   }
   else
      SysUtl.trace("Maximum edge distance is 0, distance normalization was aborted.");
   return max == 0D ? 0D : 1D / max;
}

/**
 * Removes an edge if one of it's nodes is not visible.
 */
public void removeInvisibleNodesEdges ()
{
   RemoveEdgesWithInvisibleNodes w = new RemoveEdgesWithInvisibleNodes();
   work(w);
}

/**
 * Set a root, which is place in the virtual center.
 * @param n
 */
public void setOrigin (String origin)
{
   this.origin = origin;
   nodes.add(new Node(null, this.origin));
}

/**
 * Get the origin
 */
public String getOrigin ()
{
   return origin;
}

public boolean isOrigin (String node)
{
   return origin != null && node != null && origin.equals(node);
}

/**
 * Add a node in the first level, so that it appears under root. 
 * Note that before this the root or origin has to be set.
 * @param label
 * @param distance
 */
public void add (String label, double distance, int type)
{
   if (isOrigin(label))
      return;
   if (edges.contain(origin, label))
      return;
   if (!nodes.contain(label))
      nodes.add(new Node(origin, label));
   edges.add(origin, distance, type, label);
   // OsUtl.trace("Added " + origin + " " + distance + " " + node);
}

/**
 * Add an edge.
 * Note that before this the parent node must exist.
 */
public void add (String parentlabel, double dist, int type, String childlabel) throws GraphModelException
{
   //   if (isOrigin(parentlabel)) throw new GraphModelException("Forbidden add(" + parentlabel + ", " + dist + ", " + childlabel + "), parent("
   //      + parentlabel + ") is root!");
   if (edges.contain(parentlabel, childlabel))
   {
//      SysUtl.trace("SKIPPED (" + parentlabel + ", " + childlabel + ")");
      return;
   }
   if (!nodes.contain(parentlabel))
      throw new GraphModelException("Can't add(" + parentlabel + ", " + dist + ", " + childlabel + "), because the parent(" + parentlabel
         + ") is unknown!");
   if (!nodes.contain(childlabel))
   {
      nodes.add(new Node(parentlabel, childlabel));
   }
//   else
//      SysUtl.trace("SKIPPED (" + childlabel + ")");
   edges.add(parentlabel, dist, type, childlabel);
//   SysUtl.trace("Added " + parentlabel + " " + dist + " " + childlabel);
}

public Nodes getSubNodes (String label)
{
   return edges.getChildNodes(label);
}

public double getDistance (String parentlabel, String childlabel)
{
   return edges.distance(parentlabel, childlabel);
}

/**
 * Counts the children of a node.
 * @param label
 * @return -1 if the node does not exist, else the number of child-nodes.
 */
public int countSubNodes (String label)
{
   return edges.countChildren(label);
}

// working recursivly on edges:
public void work (EdgeWorker w)
{
   WorkerController wc = new WorkerController(w, this, edges, nodes);
   wc.start();
}
}
