/*********************************************************
** Copyright (c) 2005
** University of Washington
** Licensed under the terms set forth by University of
** Washington. If you did not sign such a license, you
** are using this software/code illegally and you do not
** have permission to use, modify, or redistribute
** this or any files in this software package.
**
** File: ClusterNodeTest.h
**
**********************************************************/
#ifndef __CLUSTERNODETEST__
#define __CLUSTERNODETEST__

#include "UnitTestClass.h"
#include "ClusterNode.h"
#include "TestException.h"
#include "VectorDataFunctions.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template<class DataType> class CBuiltinStringWriter
{
 public:
  string ToString(DataType* pObj)
  {
    std::stringstream strm;
    strm << *pObj;
    return string(strm.str());
  }
};

class CVectorWriter
{
 public:
  string ToString(TDoubleVector* vec)
  {
    assert(NULL != vec);

    std::stringstream strm;

    TDoubleVector::iterator i = vec->begin();
    for(;i != vec->end(); i++)
      {
	strm << " " << (*i);
      }
    strm << "\n";
    return strm.str();
  }
};

template<class DataType, class StringWriter = class CBuiltinStringWriter<DataType> > class CLeavesToString : public CClusterNodeFunction
{
 public:
  CLeavesToString() 
  {
  }

  void Go(CClusterNode* pNode)
  {
    StringWriter writer;
    if(pNode->IsLeaf())
      {
	m_strm << writer.ToString((DataType*)pNode->GetData()) << " ";
      }
    else
      {
	m_strm << "*" << " ";
      }
  }

  string GetString()
  {
    return m_strm.str();
  }

  void Clear()
  {
    m_strm.str("");
  }

  std::stringstream m_strm;
};


class CClusterNodeTest : public CUnitTestClass
{
 public:
  CClusterNodeTest() : CUnitTestClass("ClusterNodeTest")
  {
  }

 protected:
  void RunTests()
  {
    TestClusterNode();
    TestWalkLeaves();
  }

 private:
  void TestClusterNode()
  {
    BeginTest("TestClusterNode");
    bool success = true;
    string leaf1data("leaf1");
    string leaf2data("leaf2");
    string leaf3data("leaf3");
    string leaf4data("leaf4");
    string leaf5data("leaf5");
    string parent23data("parent23");
    string parent45data("parent45");
    string parent2345data("parent2345");
    string rootdata("root");

    CClusterNode leaf1((void*)&leaf1data, 0);
    CClusterNode leaf2((void*)&leaf2data, 0);
    CClusterNode leaf3((void*)&leaf3data, 0);
    CClusterNode leaf4((void*)&leaf4data, 0);
    CClusterNode leaf5((void*)&leaf5data, 0);
    CClusterNode parent23((void*)&parent23data, 0, &leaf2, &leaf3);
    CClusterNode parent45((void*)&parent45data, 0, &leaf4, &leaf5);
    CClusterNode parent2345((void*)&parent2345data, 0, &parent23, &parent45);
    CClusterNode root((void*)&rootdata, 0, &leaf1, &parent2345);

    try
    {
    /* check parents */
      if (leaf1.GetParent() != &root)
	__throw_no_message;
      if(leaf2.GetParent() != &parent23)
	__throw_no_message;
      if(leaf3.GetParent() != &parent23)
	__throw_no_message;
      if (leaf4.GetParent() != &parent45)
	__throw_no_message;
      if (leaf5.GetParent() != &parent45)
	__throw_no_message;
      if (parent23.GetParent() != &parent2345)
	__throw_no_message;
      if (parent45.GetParent() != &parent2345)
	__throw_no_message;
      if (parent2345.GetParent() != &root)
	__throw_no_message;

      /* check child pointers */
      if (root.GetLeftChild() != &leaf1)
	__throw_no_message;
      if (root.GetRightChild() != &parent2345)
	__throw_no_message;
      if (parent2345.GetLeftChild() != &parent23)
	__throw_no_message;
      if (parent2345.GetRightChild() != &parent45)
	__throw_no_message;
      if (parent23.GetLeftChild() != &leaf2)
	__throw_no_message;
      if (parent23.GetRightChild() != &leaf3)
	__throw_no_message;
      if (parent45.GetLeftChild() != &leaf4)
	__throw_no_message;
      if (parent45.GetRightChild() != &leaf5)
	__throw_no_message;
      if (leaf1.GetLeftChild() != NULL)
	__throw_no_message;
      if (leaf1.GetRightChild() != NULL)
	__throw_no_message;
      
      if(!leaf1.IsLeaf())
	__throw_message("leaf1.IsLeaf() returned false");
      
      if(parent2345.IsLeaf())
	__throw_message("parent2345.IsLeaf() returned true");
      
      /* check Leaves collections */
      // Leaves of root should be leaf1-leaf5
      NodeVector* nv = root.GetLeftLeaves();
      if(nv->size() != 1)
	__throw_message("root does not have 1 left leaf");
      
      NodeVector::iterator iter = nv->begin();
      if ((*iter) != &leaf1)
	__throw_message("root left child is not leaf1");
      
      /* check that the number of right leaves is correct */
      nv = root.GetRightLeaves();
      if(nv->size() != 4)
	__throw_message("root does not have 4 right leaves");
      
      /* check that all the leaves are correct */
      iter = nv->begin();
      if((*iter) != &leaf2)
	__throw_message("root right leaf 1 should be leaf2");
      iter++;
      if((*iter) != &leaf3)
	__throw_message("root right leaf 2 should be leaf3");
      iter++;
      if((*iter) != &leaf4)
	__throw_message("root right leaf 3 should be leaf4");
      iter++;
      if((*iter) != &leaf5)
	__throw_message("root right leaf 4 should be leaf5");
      
      /* check SwapLeaves */
      parent2345.SwapLeaves();
      if(parent2345.GetLeftChild() != &parent45)
	__throw_message("after swap, left child of parent2345 should be parent45 and is not");
      if(parent2345.GetRightChild() != &parent23)
	__throw_message("after swap, right child of parent2345 should be parent23 and is not");

      nv = parent2345.GetLeftLeaves();
      iter = nv->begin();
      if((*iter) != &leaf4)
	__throw_message("after swap, leaf4 should be a leftmost leaf of parent2345 and is not");
      iter++;
      if((*iter) != &leaf5)
	__throw_message("after swap, leaf5 should be a leftmost leaf of parent2345 and is not");
      iter++;
      if(iter != nv->end())
	__throw_message("after swap, parent2345 has too many left leaves");

      nv = parent2345.GetRightLeaves();
      iter = nv->begin();
      if((*iter) != &leaf2)
	__throw_message("after swap, leaf2 should be a rightmost leaf of parent2345 and is not");
      iter++;
      if((*iter) != &leaf3)
	__throw_message("after swap, leaf3 should be a rightmost leaf of parent2345 and is not");
      iter++;
      if(iter != nv->end())
	__throw_message("after swap, parent2345 has too many right leaves");

    }
    catch(CTestException *pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }

    EndTest(success);
  }


  void TestWalkLeaves()
  {
    bool success = true;
    BeginTest("TestWalkLeaves");

    try
    {
      string expected = "2 7 9 0 ";
      int data[] = {2, 7, 9, 0};
      CClusterNode n2((void*)&data[0], 0);
      CClusterNode n7((void*)&data[1], 0);
      CClusterNode n9((void*)&data[2], 0);
      CClusterNode n0((void*)&data[3], 0);
      CClusterNode p27((void*)0, 0, &n2, &n7);
      CClusterNode p90((void*)0, 0, &n9, &n0);
      CClusterNode root((void*)0, 0, &p27, &p90);
      
      CLeavesToString<int> func;
      root.WalkLeaves(&func);
      if(func.GetString() != expected)
	__throw_message("leaf ordering was not as expected");
    }
    catch(CTestException* pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }

    EndTest(success);
  }
};

#endif
