/*
 * Test program for the class NTree
 *
 * $Date$
 * $Revision$
 *
 */

#ifdef NDEBUG
#undef NDEBUG
#endif /* NDEBUG */

#include <cstring>

#include "WTools/PyramidTree.hh"
#include "WImage/StillImage.hh"
#include "Wave/PyramidTransform.hh"
#include "WImage/tools.h"

#if !(defined __WATCOMC__ && __WATCOMC__ < 1230)
using namespace std;
#endif

static void testPyramidalTree (void);
static void testNTree (void);

int
main (void)
{
  testNTree ();
  testPyramidalTree ();
  return 0;
}

static void
testNTree (void)
{
  cout << "Testing class NTree...\n";

  NTree < int > *tree = NEW (NTree < int > (5));

  assert (tree->card () == 1);
  assert (tree->aryness () == 5);
  assert (tree->isRoot ());
  assert (tree->position () < 0);
  assert (!tree->hasChildren ());
  assert (!tree->hasLeftSibling ());
  assert (!tree->hasRightSibling ());

  /* let indices go out of bounds to test the indexOK() method */
  for (int i = -1; i < tree->aryness () + 1; i++)
    {
      assert (!tree->hasChildAt (i));
    }
  
  tree->data () = 42;
  assert (tree->data () == 42);

  NTree < int > *dummy = NEW (NTree < int > (5));
  dummy->data () = 13;
  
  assert (!tree->equals (*dummy));
  dummy->data () = tree->data ();
  
  assert (dummy->equals (*tree));

  /* let indices go out of bounds to test the indexOK() method */
  for (int i = -1; i < tree->aryness () + 1; i++)
    {
      try
        {
          (void)tree->childAt (i);
          assert (0);
        }
      catch (const std::exception &error)
        {
          cout << "Expected exception caught: " << error.what () << endl;
        }
    }

  try
    {
      (void)tree->leftSibling ();
      assert (0);
    }
  catch (const std::exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
    }
  try
    {
      (void)tree->rightSibling ();
      assert (0);
    }
  catch (const std::exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
    }

  NTree < int > *child = NEW (NTree < int > (5));

  try
    {
      child->data () = 13;
      tree->appendAt (0, child);
      assert (tree->hasChildAt (0));
      assert (tree->card () == 2);

      dummy->appendAt (0, 13);
      assert (tree->equals (*dummy));
      
      child = NEW (NTree < int > (5));
      child->data () = 7;
      
      (tree->childAt (0)).appendAt (0, child);
      assert ((tree->childAt (0)).hasChildAt (0));
      assert (tree->card () == 3);
      
      child = NEW (NTree < int > (5));
      child->data () = 10;
      
      tree->appendAt (1, child);
      assert (tree->hasChildAt (1));
      assert (tree->card () == 4);
    }
  catch (const exception &error)
    {
      cerr << "Exception caught! " << error.what () << endl;
      assert (0);
    }

  assert ((tree->childAt (0)).hasRightSibling ());
  assert (!(tree->childAt (0)).hasLeftSibling ());
  assert ((tree->childAt (1)).hasLeftSibling ());
  assert (!(tree->childAt (1)).hasRightSibling ());

  assert (&tree->childAt (0) == &(tree->childAt (1)).leftSibling ());
  assert (&tree->childAt (1) == &(tree->childAt (0)).rightSibling ());

  assert (!tree->equals (*dummy));

  DELETE (dummy);
  
  dummy = tree->clone ();
  assert (tree->equals (*dummy));

  // after deleting a subtree it should not be deleted again
  delete &dummy->childAt (0);
  DELETE (dummy);

  DELETE (tree);
  
  cout << "Test of class NTree completed successfully.\n";
}

static void
testPyramidalTree (void)
{
  cout << "Testing class PyramidalTree...\n";
  
  Image *image = NEW (StillImage ());
  try
    {
      image->read ("lena128.pgm");
    }
  catch (const exception &error)
    {
      cerr << "Error reading file: " << error.what () << endl;
      assert (0);
    }

  PyramidTransform *transform = NEW (PyramidTransform (*image, Daub4));

  try
    {
      transform->analysis (6);
    }
  catch (const exception &error)
    {
      cerr << "Analysis: " << error.what () <<endl;
      assert (0);
    }

  int subband;
  area channel;
  
  transform->where (0, 0, subband, channel);
  assert (strcmp (tools_areaToString (channel), "LL") == 0);
  
  int ypos = 42;
  int xpos = 42;

  // test the depth() method...
  PyramidTree *tree = NEW (PyramidTree (*transform, ypos, ypos, -1, NULL, 1));
  assert (tree->depth () == 1);

  transform->where (ypos, xpos, subband, channel);
  //cout << "The area is: " << tools_areaToString (channel) << endl;
  //cout << "The subband is: " << subband << endl;
  assert (channel != LL);
  assert (subband > 1);
  assert (tree->isRoot ());

  int yoffs1, xoffs1, ysize1, xsize1;
  transform->where (channel, subband, yoffs1, xoffs1, ysize1, xsize1);

  //cout << "The offset is: " << yoffs1 << ", " << xoffs1 << endl;
  //cout << "The size is: " << ysize1 << ", " << xsize1 << endl;

  // test the addGenerations() method (also tests findChildPosition())...
  tree->addGenerations (1);
  assert (tree->depth () == 2);

  for (int i = 0; i < 4; i++)
    {
      assert (tree->hasChildAt (i));
    }

  int yoffs2 = yoffs1 * 2;
  int xoffs2 = xoffs1 * 2;
  int ysize2 = ysize1 * 2;
  int xsize2 = xsize1 * 2;
  
  CoeffInformation c = (tree->childAt (0)).data ();

  assert ((ypos - yoffs1) / ysize1 == (c.ypos () - yoffs2) / ysize2);
  assert ((xpos - xoffs1) / xsize1 == (c.xpos () - xoffs2) / xsize2);
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (1)).data ()).ypos ());
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (1)).data ()).xpos () - 1);
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (2)).data ()).ypos () - 1);
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (2)).data ()).xpos ());
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (3)).data ()).ypos () - 1);
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (3)).data ()).xpos () - 1);

  
  c.dump ();
  cout << endl;

  // test the moveTo() method
  ypos = 43;
  xpos = 43;
  tree->moveTo (ypos, xpos);
  c = tree->data ();
  
  assert (c.ypos () == ypos);
  assert (c.xpos () == xpos);
  
  for (int i = 0; i < 4; i++)
    {
      assert (tree->hasChildAt (i));
      c = (tree->childAt (i)).data ();
      //cout << c.ypos () << " " << c.xpos () << endl;
      assert ((ypos - yoffs1) / ysize1 == (c.ypos () - yoffs2) / ysize2);
      assert ((xpos - xoffs1) / xsize1 == (c.xpos () - xoffs2) / xsize2);
    }
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (1)).data ()).ypos ());
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (1)).data ()).xpos () - 1);
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (2)).data ()).ypos () - 1);
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (2)).data ()).xpos ());
  assert (((tree->childAt (0)).data ()).ypos () == ((tree->childAt (3)).data ()).ypos () - 1);
  assert (((tree->childAt (0)).data ()).xpos () == ((tree->childAt (3)).data ()).xpos () - 1);

  DELETE (tree);
  DELETE (image);
  DELETE (transform);

  cout << "Test of class PyramidalTree completed successfully.\n";
}
