//-------------------------------------------------------------------------------------------------
// Module    : $RCSfile: TaskTreeImplTest.java,v $
// Version   : $Revision: 0.0 $  $Author: patrick $  $Date: 02.04.2012 $
// Project   : TaskTreeImpl
// Creation  : 2012 by patrick
// Copyright : Patrick Harms, 2012
//-------------------------------------------------------------------------------------------------
package de.ugoe.cs.quest.tasktrees.treeimpl;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

import de.ugoe.cs.quest.tasktrees.testutils.DummyGUIElement;
import de.ugoe.cs.quest.tasktrees.testutils.DummyInteraction;
import de.ugoe.cs.quest.tasktrees.treeifc.InteractionTask;
import de.ugoe.cs.quest.tasktrees.treeifc.Iteration;
import de.ugoe.cs.quest.tasktrees.treeifc.NodeInfo;
import de.ugoe.cs.quest.tasktrees.treeifc.Selection;
import de.ugoe.cs.quest.tasktrees.treeifc.Sequence;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTree;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeBuilder;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeNode;
import de.ugoe.cs.quest.tasktrees.treeifc.TaskTreeNodeFactory;

//-------------------------------------------------------------------------------------------------
/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 02.04.2012$
 * @author 2012, last modified by $Author: patrick$
 */
//-------------------------------------------------------------------------------------------------
public class TaskTreeImplTest
{
  /** */
  private static final int MAX_TREE_DEPTH = 15;
  
  /** */
  private TaskTreeBuilder mTaskTreeBuilder = new TaskTreeBuilderImpl();
  
  /** */
  private TaskTreeNodeFactory mTaskTreeNodeFactory = new TaskTreeNodeFactoryImpl();
  
  //-----------------------------------------------------------------------------------------------
  /**
   * @throws Exception 
   * 
   */
  //-----------------------------------------------------------------------------------------------
  @Test
  public void testRandomTrees() throws Exception
  {
    int noOfTrees = 10;
    int noOfMaxChildren = 8;
    int maxDepth = MAX_TREE_DEPTH;
    
    for (int i = 0; i < noOfTrees; i++)
    {
      System.err.println("iteration " + (i + 1) + ":");
      System.err.println("  creating tree");
      Map<TaskTreeNode, NodeInfo> treeInfos = new HashMap<TaskTreeNode, NodeInfo>();
      TaskTreeNode rootNode = createTree(noOfMaxChildren, maxDepth, treeInfos);
      System.err.println("  creating task tree");
      TaskTree taskTree = mTaskTreeNodeFactory.createTaskTree(rootNode);
      
      System.err.println("  validating task tree");
      assertEquals(rootNode, taskTree.getRoot());
      assertMapsEqual(treeInfos, taskTree.getTaskMap());
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @param taskMap
   */
  //-----------------------------------------------------------------------------------------------
  private void assertMapsEqual(Map<TaskTreeNode, NodeInfo> map1,
                               Map<TaskTreeNode, NodeInfo> map2)
  {
    try
    {
      if (map1 == null)
      {
        assertNull(map2);
        return;
      }
      
      assertEquals(map1.size(), map2.size());
      
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : map1.entrySet())
      {
        NodeInfo value2 = map2.get(entry.getKey());
        assertNotNull(value2);
        assertEquals(entry.getValue().getTask(), value2.getTask());
        assertEquals(entry.getValue().getNoOfOccurencesInTree(), value2.getNoOfOccurencesInTree());
      }
    }
    catch (AssertionError e)
    {
      dumpMap(map1);
      dumpMap(map2);
      throw e;
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param map2
   */
  //-----------------------------------------------------------------------------------------------
  private void dumpMap(Map<TaskTreeNode, NodeInfo> map)
  {
    System.err.println();
    
    if (map == null)
    {
      System.err.println("map is null");
    }
    else
    {
      System.err.println("map:");
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : map.entrySet())
      {
        System.err.print("  ");
        System.err.print(entry.getKey());
        for (int i = entry.getKey().toString().length(); i < 49; i++)
        {
          System.err.print(" ");
        }
        System.err.print(" : ");
        System.err.println(entry.getValue());
      }
    }
    
    System.err.println();
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param noOfMaxChildren
   * @param maxDepth
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private TaskTreeNode createTree(int                         maxNoOfChildren,
                                  int                         maxDepth,
                                  Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    /*for (int i = 0; i < (MAX_TREE_DEPTH + 1 - maxDepth); i++)
    {
      System.err.print("  ");
    }*/
    
    TaskTreeNode tree;
    
    // integrating the maximum depth here assures, that either something between 0 and 8 will
    // be the type, or if the max depth decreases near 0 only interaction tasks will be created
    // to finish the tree creation
    int type = (int) (Math.random() * (Math.min(8, maxDepth)));
    
    switch (type)
    {
      case 0:
      {
        //System.err.print("creating new interaction task ");
        tree = createNewInteractionTask(treeInfos);
        break;
      }
      case 1:
      {
        //System.err.print("reusing interaction task ");
        tree = reuseInteractionTask(treeInfos);
        break;
      }
      case 2:
      {
        //System.err.println("creating new sequence {");
        tree = createNewSequence(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      case 3:
      {
        //System.err.println("reusing sequence {");
        tree = reuseSequence(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      case 4:
      {
        //System.err.println("creating new selection {");
        tree = createNewSelection(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      case 5:
      {
        //System.err.println("reusing selection {");
        tree = reuseSelection(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      case 6:
      {
        //System.err.println("creating new iteration {");
        tree = createNewIteration(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      case 7:
      {
        //System.err.println("reusing iteration {");
        tree = reuseIteration(maxNoOfChildren, maxDepth, treeInfos);
        break;
      }
      default:
      {
        //System.err.print("creating new interaction task per default ");
        tree = createNewInteractionTask(treeInfos);
      }
    }
    
    /*
    if (!(tree instanceof InteractionTask))
    {
      for (int i = 0; i < (MAX_TREE_DEPTH + 1 - maxDepth); i++)
      {
        System.err.print("  ");
      }
      
      System.err.print("} ");
    }
    
    System.err.println(tree);*/
    
    return tree;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private InteractionTask createNewInteractionTask(Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    Thread.sleep(2);
    long id = System.currentTimeMillis();
    InteractionTask task = mTaskTreeNodeFactory.createNewInteractionTask
      (new DummyGUIElement("elem" + id), new DummyInteraction("interaction" + id, 1));
    
    treeInfos.put(task, new NodeInfoImpl(task));
    
    return task;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private InteractionTask reuseInteractionTask(Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    int noOfInteractionTasks = 0;
    
    for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
    {
      if (entry.getKey() instanceof InteractionTask)
      {
        noOfInteractionTasks++;
      }
    }
    
    if (noOfInteractionTasks > 0)
    {
      noOfInteractionTasks = (int) (Math.random() * noOfInteractionTasks);
      
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
      {
        if (entry.getKey() instanceof InteractionTask)
        {
          if (--noOfInteractionTasks <= 0)
          {
            return (InteractionTask) entry.getKey();
          }
        }
      }
    }
    else
    {
      return createNewInteractionTask(treeInfos); 
    }
    
    throw new RuntimeException("this is an implementation error");
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Sequence createNewSequence(int                         maxNoOfChildren,
                                     int                         maxDepth,
                                     Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    Sequence sequence = mTaskTreeNodeFactory.createNewSequence();
    
    int noOfChildren = (int) (Math.random() * maxNoOfChildren);
    
    for (int i = 0; i < noOfChildren; i++)
    {
      TaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
      
      // through first removing an existing parent it is assured, that a parent is recorded
      // only once. This is needed, because parent may be reused in a tree as well, but we always
      // iterate the whole tree
      ((NodeInfoImpl) treeInfos.get(child)).removeParent(sequence);
      ((NodeInfoImpl) treeInfos.get(child)).addParent(sequence);
      mTaskTreeBuilder.addChild(sequence, child);
    }
    
    treeInfos.put(sequence, new NodeInfoImpl(sequence));
    return sequence;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Sequence reuseSequence(int                         maxNoOfChildren,
                                 int                         maxDepth,
                                 Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    int noOfSequences = 0;
    
    for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
    {
      if (entry.getKey() instanceof Sequence)
      {
        noOfSequences++;
      }
    }
    
    if (noOfSequences > 0)
    {
      noOfSequences = (int) (Math.random() * noOfSequences);
      
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
      {
        if (entry.getKey() instanceof Sequence)
        {
          if (--noOfSequences <= 0)
          {
            return (Sequence) entry.getKey();
          }
        }
      }
    }
    else
    {
      return createNewSequence(maxNoOfChildren, maxDepth, treeInfos); 
    }
    
    throw new RuntimeException("this is an implementation error");
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Selection createNewSelection(int                         maxNoOfChildren,
                                       int                         maxDepth,
                                       Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    Selection selection = mTaskTreeNodeFactory.createNewSelection();
    
    int noOfChildren = (int) (Math.random() * maxNoOfChildren);
    
    for (int i = 0; i < noOfChildren; i++)
    {
      TaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
      
      // through first removing an existing parent it is assured, that a parent is recorded
      // only once. This is needed, because parent may be reused in a tree as well, but we always
      // iterate the whole tree
      ((NodeInfoImpl) treeInfos.get(child)).removeParent(selection);
      ((NodeInfoImpl) treeInfos.get(child)).addParent(selection);
      mTaskTreeBuilder.addChild(selection, child);
    }
    
    treeInfos.put(selection, new NodeInfoImpl(selection));
    return selection;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Selection reuseSelection(int                         maxNoOfChildren,
                                   int                         maxDepth,
                                   Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    int noOfSelections = 0;
    
    for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
    {
      if (entry.getKey() instanceof Selection)
      {
        noOfSelections++;
      }
    }
    
    if (noOfSelections > 0)
    {
      noOfSelections = (int) (Math.random() * noOfSelections);
      
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
      {
        if (entry.getKey() instanceof Selection)
        {
          if (--noOfSelections <= 0)
          {
            return (Selection) entry.getKey();
          }
        }
      }
    }
    else
    {
      return createNewSelection(maxNoOfChildren, maxDepth, treeInfos); 
    }
    
    throw new RuntimeException("this is an implementation error");
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Iteration createNewIteration(int                         maxNoOfChildren,
                                       int                         maxDepth,
                                       Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    Iteration iteration = mTaskTreeNodeFactory.createNewIteration();
    
    TaskTreeNode child = createTree(maxNoOfChildren, maxDepth - 1, treeInfos);
    
    // through first removing an existing parent it is assured, that a parent is recorded
    // only once. This is needed, because parent may be reused in a tree as well, but we always
    // iterate the whole tree
    ((NodeInfoImpl) treeInfos.get(child)).removeParent(iteration);
    ((NodeInfoImpl) treeInfos.get(child)).addParent(iteration);
    mTaskTreeBuilder.setChild(iteration, child);
    
    treeInfos.put(iteration, new NodeInfoImpl(iteration));
    return iteration;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param treeInfos
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private Iteration reuseIteration(int                         maxNoOfChildren,
                                   int                         maxDepth,
                                   Map<TaskTreeNode, NodeInfo> treeInfos)
    throws Exception
  {
    int noOfIterations = 0;
    
    for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
    {
      if (entry.getKey() instanceof Iteration)
      {
        noOfIterations++;
      }
    }
    
    if (noOfIterations > 0)
    {
      noOfIterations = (int) (Math.random() * noOfIterations);
      
      for (Map.Entry<TaskTreeNode, NodeInfo> entry : treeInfos.entrySet())
      {
        if (entry.getKey() instanceof Iteration)
        {
          if (--noOfIterations <= 0)
          {
            return (Iteration) entry.getKey();
          }
        }
      }
    }
    else
    {
      return createNewIteration(maxNoOfChildren, maxDepth, treeInfos); 
    }
    
    throw new RuntimeException("this is an implementation error");
  }

}
