/*
 * Test program for class VideoArray
 *
 * $Date$
 * $Revision$
 *
 */

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

/* turn off debug output from tested class... */
#ifndef DPRINTF
#define DPRINTF(x)
#endif

#include <cassert>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstring>
#include "WImage/debug.h"
#include "WImage/VideoFrame.hh"
#include "WImage/ColorVideo.hh"
#include "Wave/PyramidTransform.hh"
#include "Wave/Filter.hh"

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

static void 
dump (VideoArray<int> *va)
{
  int current = va->current ();
  for (int frame = 0; frame < va->frames (); frame++)
    {
      va->current (frame);
      cout << "frame: " << frame << endl;
      for (int row = 0; row < va->rows (); row++)
        {
          cout << "[" << std::setw (2) << std::setfill (' ') << row << "] ";
          for (int col = 0; col < va->cols (); col++)
            {
              cout << va->at (row, col) << ' ';
            }
          cout << endl;
        }
    }
  va->current (current);
}

int
test_videoArray (void)
{
  cout << "Testing class VideoArray...\n";

  int x = 0, y = 0, n = 3;
  VideoArray<int> *va = NEW (VideoArray<int> (y, x, n));
  assert (va != NULL);
  DELETE (va);

  x = 3, y = 3;
  va = NEW (VideoArray<int> (y, x, n));

  /*
   * Constructor, video export and attributes test...
   */
  assert (va->rows () == y);
  assert (va->cols () == x);
  assert (va->size () == x*y);
  assert (va->frames () == n);

  /*
   * Writing video member reference...
   */
  va->to (0, 1, 1);
  assert (va->at (0, 1) == 1);

  /*
   * Fill the video's frames...
   */
  va->current (0);
  for (int i = 0; i < va->size (); i++)
    {
      va->to (i, 10 * i);
      assert (va->at (i) == 10 * i);
      assert (va->array () [i] == 10 * i);
    }
  for (int frame = 1; frame < n; frame++)
    {
      va->current (frame);
      for (int i = 0; i < va->size (); i++)
        {
          va->to (i, i);
          assert (va->at (i) == i);
          assert (va->array () [frame * va->size () + i] == i);
        }
    }
  cout << va->smax () << " " << va->amax () << endl;
  
  assert (va->smax () == va->size () -1);
  assert (va->amax () == va->size () -1);


  va->to (0, 0, (va->at (va->rows () - 1, va->cols () - 1) + 1) * (-1));
  assert (va->smin () == -1 * va->amax ());
  va->to (0, 0, (va->at (0, 0) * (-1) - 1));
  assert (va->smin () == va->amin ());
  assert (va->amin () == va->at (0, 1));
  va->to (0, 0, 0);

  assert (va->equalsFrames (2, 1));
  assert (!va->equalsFrames (0, 1));
  assert (!va->equalsFrames (0, 2));

  /* test access methods */
  for (int frame = 0; frame < va->frames (); frame++)
    {
      va->current (frame);
      for (int y = 0; y < va->rows (); y++)
        {
          for (int x = 0; x < va->cols (); x++)
            {
              int oldval = va->at (y, x);
              va->to (y, x, 42);
              assert (va->at (y, x) == 42);
              assert (va->at (y * va->cols () + x));
              va->to (y * va->cols () + x, 42);
              assert (va->at (y, x) == 42);
              assert (va->at (y * va->cols () + x));
              va->to (y, x, oldval);
            }
        }
    }

  try 
    {
      va->current (-1);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "bounds") != NULL);
    }
  try 
    {
      va->current (n);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "bounds") != NULL);
    }

  try 
    {
      va->resize (0, -1);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "negative") != NULL);
    }

  /*
   * Test the clone() method. Values must be the same, but not the 
   * addresses... 
   * We can now also test the equals () method...
   */
  VideoArray <int> *va2 = va->cloneVideo ();
  assert (va2 != NULL);
  assert (va->rows () == va2->rows ());
  assert (va->cols () == va2->cols ());
  assert (va->size () == va2->size ());
  assert (va->frames () == va2->frames ());
  
  for (int frame = 0; frame < va->frames (); frame++)
    {
      va->current (frame);
      va2->current (frame);
      for (int row = 0; row < va->rows (); row++)
        {
          for (int col = 0; col < va->cols (); col++)
            {
              assert (va->at (row, col) == va2->at (row, col));
            }
        }
    }
  assert (va->equals (*va2) && va2->equals (*va));

  /*
   * The resize method can be tricky...
   */
  va->resize (y+1, x+1);
  assert (va->rows () == y+1);
  assert (va->cols () == x+1);
  assert (va->size () == (x+1)*(y+1));

  /*
   * We now have two different arrays!
   */
  assert (!va->equals (*va2));
  //dump (va);

  /*
   * When growing, the line ends and empty lines get filled with 0s.
   * After resizing (shrinking) to original size it should be the same
   * as the clone again.
   */
  for (int i = 0; i < va->rows () - 1; i++)
    {
      //dump (va);
      assert (va->at (i, x) == 0);
    }
  for (int i = 0; i < va->cols (); i++)
    {
      assert (va->at (va->rows () - 1, i) == 0);
    }

  /*dump (va);*/
  va->resize (y, x);
  /*dump (va2);
    dump (va);*/
  assert (va->equals (*va2));

  dump (va2);
  cout << endl;
  va2->reframe (n - 1);
  assert (!va->equals (*va2));
  assert (!va2->equals (*va));
  va2->reframe (n);
  assert (!va->equals (*va2));
  assert (!va2->equals (*va));
  va2->current (n - 1);
  for (int y = 0; y < va2->rows (); y++)
    {
      for (int x = 0; x < va2->cols (); x++)
        {
          assert (va2->at (y, x) == 0);
        }
    }
  va2->copy (*va);
  assert (va->equals (*va2));
  assert (va2->equals (*va));
  
  /*
   * Now we resize to not overall bigger and not overall smaller...
   * First pass: 2 rows, 4 columns:
   */
  va->resize (y-1, x+1);
  assert (va->rows () == y-1);
  assert (va->cols () == x+1);
  assert (va->size () == (x+1)*(y-1));
  assert (!va->equals (*va2));
  assert (va->at (0, x) == 0 && va->at (1, x) == 0);
  va2->copy (*va);
  assert (va2->equals (*va));
  assert (va->equals (*va2));

  /*
   * Second pass: 4 rows, 2 columns:
   */
  va->resize (y+1, x-1);
  assert (va->rows () == y+1);
  assert (va->cols () == x-1);
  assert (va->size () == (x-1)*(y+1));
  assert (!va->equals (*va2));
  assert (va->at (y-1, 0) == 0 && va->at (y-1, 1) == 0);
  assert (va->at (y, 0) == 0 && va->at (y, 1) == 0);

  /*
   * Third pass: produce a real big one:
   */
  x = y = 40;
  va->resize (y, x);
  assert (va->rows () == y);
  assert (va->cols () == x);
  assert (va->size () == y*x);
  assert (!va->equals (*va2));
  assert (va->at (y-1, 0) == 0 && va->at (0, x-1) == 0);
  assert (va->at (y-1, x-1) == 0);
  va->to (y-1, x-1, 1);
  va->to (0, 0, 9);

  /*
   * Forth pass: produce a real small one:
   */

  x = y = 1;
  va->resize (y, x);
  assert (va->rows () == y);
  assert (va->cols () == x);
  assert (va->size () == y*x);
  assert (va->at (0, 0) == 9);
  
  /* Try out asymmetric resizing and then compare: find row/col 
   * messup if there is any */
  va->resize (2 * y, x);
  VideoArray<int> *dummy = va->cloneVideo ();
  assert (dummy->equals (va));
  DELETE (dummy);
  va->resize (y, 2 * x);
  dummy = va->cloneVideo ();
  assert (dummy->equals (va));
  DELETE (dummy);

  /*
   * Final pass: exception handling in resize()
   */
  try 
    {
      va->resize (0, -1);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "negative") != NULL);
    }
  try 
    {
      va->resize (-1, 0);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "negative") != NULL);
    }
  try 
    {
      va->resize (-1, -1);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "negative") != NULL);
    }
  try 
    {
      va->resize (0, 0);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "zero") != NULL);
    }
  
  try 
    {
      ImageArray <int> ia (1, 1);
      va2->copy (ia);
      ImageArray < int > *ia2 = va2->clone ();
      assert (ia2->equals (ia));
      DELETE (ia2);
      assert (0);
    }
  catch (const exception &error)
    {
      cout << "Expected exception caught: " << error.what () << endl;
      assert (strstr (error.what (), "not a VideoArray") != NULL);
    }
  DELETE (va2);

  dump (va);

  va->resize (5, 4);
  for (int i = 0; i < va->size (); i++)
    {
      va->to (i, i+1);
    }

  assert (va->saverage () == 10);
  assert (va->aaverage () == 10);
  assert (va->saverage (0, 0, -1, -1) == 10);
  assert (va->saverage (0, 0, 1, 1) == 1);
  assert (va->saverage (4, 3, -1, -1) == 20);
  
  DELETE (va);

  cout << "Test of class VideoArray completed successfully.\n";
  return 0;
}

int 
test_videoFrame (void)
{
  cout << "Testing class VideoFrame...\n";
  VideoArray < coeff > *va = NEW (VideoArray < coeff > (3, 4, 5));
  VideoFrame *frame = NEW (VideoFrame (va));
  Image *image = frame->clone ();
  assert (image->epsilons (*frame, COEFF_EPSILON));
  assert (frame->epsilons (*image, COEFF_EPSILON));
  DELETE (frame);
  DELETE (va);
  DELETE (image);
  cout << "Test of class VideoFrame completed successfully.\n";
  return 0;
}

int 
test_colorVideo (void)
{
  cout << "Testing class ColorVideo...\n";
  ColorVideo *vid = NEW (ColorVideo ());
  DELETE (vid);
  int c = 3;
  int y = 4;
  int x = 5;
  int z = 6;
  vid = NEW (ColorVideo (c, cm_rgb, y, x, z));
  Image *img = NULL;
  
  assert (vid->rows () == y);
  assert (vid->cols () == x);
  assert (vid->frames () == z);

  for (int f = 0; f < z; f++)
    {
      vid->current (f);
      for (int i = 0; i < c; i++)
        {
          img = &vid->currentFrameChannel (i);
          for (int j = 0; j < img->size (); j++)
            {
              assert (fabs (img->at (j)) < COEFF_EPSILON);
            }
        }
    }

  const char *fname = "harin.vid";
  c = 1;
  y = 240;
  x = 320;
  z = 11;

  try
    {
      vid->read (fname, 0, z, y, x, c, cm_rgb, 512);
      vid->current (0);
      img = &vid->currentFrameChannel (0);
      Image *i = img->clone ();
      DELETE (i);
    }
  catch (const exception &error)
    {
      cerr << "Exception caught: " << error.what () << endl;
      assert (0);
    }

  assert (vid->rows () == y);
  assert (vid->cols () == x);
  assert (vid->frames () == z);
  assert (vid->colors () == c);

  img = &vid->currentFrameChannel (0);
  ColorImage *ci = &vid->currentFrame ();
  assert (ci->colors () == c);
  assert ((ci->channel (0)).epsilons (*img, COEFF_EPSILON));
  assert (img->epsilons (ci->channel (0), COEFF_EPSILON));

  vid->current (5);

  assert (img->rows () == y);
  assert (img->cols () == x);

  img->write ("harin5.pgm");
  Image *copy = img->clone ();

  assert (!vid->epsilonsFrames (0, 5, 0.1));
  WaveletTransform *t = NEW (PyramidTransform (*img, Villa3));
  int nsteps = 2;
  
  cout << "Extracting frames, decomposing and reconstructing them..." << endl;

  try
    {
      t->analysis (nsteps);
    }
  catch (const exception &error)
    {
      cerr << "Analysis: " << error.what () <<endl;
      return 1;
    }

  img->write ("harin5trans.pgm");

  try
    {
      t->synthesis (nsteps, nsteps);
    }
  catch (const exception &error)
    {
      cerr << "Synthesis: " << error.what () <<endl;
      return 1;
    }
  assert (copy->epsilons (*img, 0.01));
  assert (img->epsilons (*copy, 0.01));

  img->write ("harin5recon.pgm");

  StillImage si;
  si.read ("harin5recon.pgm");
  assert (copy->equals (si));
  assert (si.equals (*copy));
  
  int yoffs, xoffs, ysize, xsize;
  try
    {
      t->analysis (nsteps);
    }
  catch (const exception &error)
    {
      cerr << "Analysis: " << error.what () <<endl;
      return 1;
    }
  t->where (LL, nsteps, yoffs, xoffs, ysize, xsize);
  img->beautify (yoffs, xoffs, ysize, xsize);
  img->write ("harin5beaut.pgm");

  img->paste (0, 0, *copy);
  assert (copy->equals (*img));
  assert (img->equals (*copy));

  cout << "Cloning, reading and writing videos..." << endl;

  ColorVideo *cv2 = vid->clone ();
  assert (cv2->equals (*vid));
  assert (vid->equals (*cv2));
  vid->read (fname, 0, 0, y, x, c, cm_rgb, 512);
  assert (vid->frames () == z);
  vid->current (0);
  img = &vid->currentFrameChannel (0);
  Image *i = img->clone ();
  DELETE (i);

  vid->write ("harin-copy.vid", 512, false);
  cv2->read ("harin-copy.vid", 0, z, y, x, c, cm_rgb, 512);
  assert (cv2->rows () == y);
  assert (cv2->cols () == x);
  assert (cv2->frames () == z);
  assert (cv2->colors () == c);

  assert (cv2->equals (*vid));
  assert (vid->equals (*cv2));

  DELETE (cv2);
  DELETE (t);
  
  cout << "Decomposing and writing videos..." << endl;

  vid->current (0);
  img = &vid->currentFrameChannel ();

  t = NEW (PyramidTransform (*img, Villa3));

  for (int i = 0; i < z; i++)
    {
      try
        {
          vid->current (i);
          t->analysis (nsteps);
        }
      catch (const exception &error)
        {
          cerr << "Exception caught: " << error.what () << endl;
          assert (0);
        }
      
    }
  vid->current (10);

  img->write ("harin10trans.pgm");
  
  vid->write ("harin-twosteps.vid", 512, true);

  t->where (LL, nsteps, yoffs, xoffs, ysize, xsize);
  vid->beautify (yoffs, xoffs, ysize, xsize);

  img->write ("harin10beaut.pgm");

  DELETE (t);
  DELETE (copy);
  DELETE (vid);

  cout << "Color videos (avi files)..." << endl;
  vid = NEW (ColorVideo ());

  try
    {
      vid->read ("twoframes.avi");
    }
  catch (const exception &error)
    {
      cerr << "Exception caught: " << error.what () << endl;
      assert (0);
    }
  
  vid->write ("temp-twoframes.avi");
  ColorVideo *vid2 = NEW (ColorVideo ());
  try
    {
      vid2->read ("temp-twoframes.avi");
    }
  catch (const exception &error)
    {
      cerr << "Exception caught: " << error.what () << endl;
      assert (0);
    }
  assert (vid->equals (*vid2));

  vid->current (1);
  vid2->current (1);

  ColorImage *c1 = &vid->currentFrame ();
  ColorImage *c2 = &vid2->currentFrame ();

  assert (c1->equals (*c2));

  DELETE (vid2);
  DELETE (vid);

  cout << "Test of class ColorVideo completed successfully.\n";
  return 0;
}


int 
main (void)
{
  assert (test_videoArray () == 0);
  assert (test_videoFrame () == 0);
  assert (test_colorVideo () == 0);
  return 0;
}

