/*********************************************************
** 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: CallbackTest.h
**
**********************************************************/
#ifndef CALLBACKTEST_H
#define CALLBACKTEST_H

#include "UnitTestClass.h"
#include "TestException.h"
#include "BasicCluster.h"
#include "ClusterTest.h"
#include "ProgressThread.h"
#include "ClusterThread.h"

#include <iostream>
#include <sstream>
#ifdef WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif

using namespace std;

std::stringstream g_sstr(stringstream::in | stringstream::out);
void ClusterCallback(void *pArg, double max, double current, int msgid)
{
  g_sstr.precision(3);
  g_sstr << "/ 0 " << max << " " << current << " " << msgid << " ";
}

class CCallbackTest : public CUnitTestClass
{
 public:
  CCallbackTest() : CUnitTestClass("CallbackTest")
  {
  }

 protected:
  void RunTests()
  {
    TestClusterCallback();
  }

 private:

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

    try
    {
      string expected =" 9\n  8\n  7\n  6\n  5\n  4\n  3\n  2\n  1\n ";
      string expectedCallbackString = "/ 0 40 0 0 / 0 108 0 1 / 0 108 108 1 / 0 81 0 2 / 0 81 1 2 / 0 81 2 2 / 0 81 3 2 / 0 81 4 2 / 0 81 5 2 / 0 81 6 2 / 0 81 7 2 / 0 81 8 2 / 0 81 9 2 / 0 81 10 2 / 0 81 11 2 / 0 81 12 2 / 0 81 13 2 / 0 81 14 2 / 0 81 15 2 / 0 81 16 2 / 0 81 17 2 / 0 81 18 2 / 0 81 19 2 / 0 81 20 2 / 0 81 21 2 / 0 81 22 2 / 0 81 23 2 / 0 81 24 2 / 0 81 25 2 / 0 81 26 2 / 0 81 27 2 / 0 81 28 2 / 0 81 29 2 / 0 81 30 2 / 0 81 31 2 / 0 81 32 2 / 0 81 33 2 / 0 81 34 2 / 0 81 35 2 / 0 81 36 2 / 0 81 37 2 / 0 81 38 2 / 0 81 39 2 / 0 81 40 2 / 0 81 41 2 / 0 81 42 2 / 0 81 43 2 / 0 81 44 2 / 0 20 0 3 / 0 20 1 3 / 0 20 2 3 / 0 20 3 3 / 0 20 4 3 / 0 20 5 3 / 0 20 6 3 / 0 20 7 3 / 0 20 8 3 / 0 20 9 3 / 0 20 10 3 / 0 20 11 3 / 0 20 12 3 / 0 20 13 3 / 0 20 14 3 / 0 20 15 3 / 0 20 16 3 / 0 20 17 3 / 0 20 18 3 / 0 20 19 3 / 0 17 0 4 / 0 17 1 4 / 0 17 2 4 / 0 17 3 4 / 0 17 4 4 / 0 17 5 4 / 0 17 6 4 / 0 17 7 4 / 0 17 8 4 / 0 17 9 4 / 0 17 10 4 / 0 17 11 4 / 0 17 12 4 / 0 17 13 4 / 0 17 14 4 / 0 17 15 4 / 0 17 16 4 ";
      /// 0 40 0 0 / 0 108 0 1 / 0 108 108 1 / 0 17 0 2 / 0 17 1 2 / 0 17 2 2 / 0 17 3 2 / 0 17 4 2 / 0 17 5 2 / 0 17 6 2 / 0 17 7 2 / 0 17 8 2 / 0 17 9 2 / 0 17 10 2 / 0 17 11 2 / 0 17 12 2 / 0 17 13 2 / 0 17 14 2 / 0 17 15 2 / 0 17 16 2 / 0 20 0 3 / 0 20 1 3 / 0 20 2 3 / 0 20 3 3 / 0 20 4 3 / 0 20 5 3 / 0 20 6 3 / 0 20 7 3 / 0 20 8 3 / 0 20 9 3 / 0 20 10 3 / 0 20 11 3 / 0 20 12 3 / 0 20 13 3 / 0 20 14 3 / 0 20 15 3 / 0 20 16 3 / 0 20 17 3 / 0 20 18 3 / 0 20 19 3 / 0 17 0 4 / 0 17 1 4 / 0 17 2 4 / 0 17 3 4 / 0 17 4 4 / 0 17 5 4 / 0 17 6 4 / 0 17 7 4 / 0 17 8 4 / 0 17 9 4 / 0 17 10 4 / 0 17 11 4 / 0 17 12 4 / 0 17 13 4 / 0 17 14 4 / 0 17 15 4 / 0 17 16 4 ";

      double pData[] = {5, 7, 1, 2, 4, 9, 8, 3, 6};
      TDoubleVector* data[9];
      for(int i = 0 ; i < 9 ; i++)
	{
	  data[i] = new TDoubleVector();
	  data[i]->push_back(pData[i]);
	}

      CEuclidean distanceFunc;
      CCentroidLinkage combineFunc;
      CLeavesToString<TDoubleVector, CVectorWriter> func;
      
      CBasicCluster cluster((CDistanceFunction*)&distanceFunc, 
			    (CCombineDataFunction*)&combineFunc);
      cluster.SetCallback(ClusterCallback, &cluster);

      COptimalOrdering ordering;
      ordering.SetCallback(ClusterCallback, &ordering);

      CClusterThread t(&cluster, data, 9, &ordering, false);
      int status = t.Start();
      while(!t.GetIsFinished())
	{
#ifdef WIN32
	  Sleep(100);
#else
	  usleep(100000);
#endif
	}

      CClusterNode* root = t.GetResult();

      CProgressThreadManager::Erase(&t);

      root->WalkLeaves(&func);
      if(func.GetString() != expected)
	{
	  cout << "Expected: " << expected << "\n";
	  cout << "Actual  : " << func.GetString() << "\n";
	  __throw_message("leaf ordering was not as expected");
	}
      
      if(g_sstr.str() != expectedCallbackString)
	{
	  cout << "Callback string         : " << g_sstr.str() << "\n";
	  cout << "Expected callback string: " << expectedCallbackString << "\n";
	  __throw_message("callback string was not as expected");
	}

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

    EndTest(success);
  }

};

#endif
