/*
 * Implementation of class ImageInformation
 *
 * $Date$
 * $Revision$
 *
 */

#include "WTools/ImageInformation.hh"
#include <cmath>
#include <cstdlib>
#include <cassert>
#ifndef _WIN32_WCE
#include <iostream>
#include <iomanip>
#endif

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

static bool
valless (const CoeffInformation *c1, const CoeffInformation *c2);
static bool
avalless (const CoeffInformation *c1, const CoeffInformation *c2);
static bool
posless (const CoeffInformation *c1, const CoeffInformation *c2);
static bool
yxless (const CoeffInformation *c1, const CoeffInformation *c2);
static bool
xyless (const CoeffInformation *c1, const CoeffInformation *c2);
static bool
idless (const CoeffInformation *c1, const CoeffInformation *c2);


ImageInformation::ImageInformation (int size)
{
  m_size = size;
  if (m_size > 0)
    {
      m_coeffs = NEW (CoeffInformation[m_size]);
    }
  else
    {
      m_coeffs = NULL;
    }
}


ImageInformation::ImageInformation (Image &img)
{
  int size = 0;
  m_coeffs = NEW (CoeffInformation[img.size ()]);
  for (int row = 0; row < img.rows (); row++)
    {
      for (int col = 0; col < img.cols (); col++, size++)
        {
          m_coeffs[size].set (img.at (row, col), row, col, img.abs (row, col));
        }
    }
  m_size = size;
}

ImageInformation::~ImageInformation (void)
{
  DELETENOTNULLAR (m_coeffs);
}

ImageInformation *
ImageInformation::head (int n)
{
  return subimage (n, true);
}

ImageInformation *
ImageInformation::tail (int n)
{
  return subimage (n, false);
}

ImageInformation *
ImageInformation::append (ImageInformation &ii)
{
  ImageInformation *rval = this->clone ();
  rval->resize (m_size + ii.size ());
  for (int i = 0; i < ii.size (); i++)
    {
      rval->to (ii.at (i), m_size + i);
    }
  return rval;
}

void
ImageInformation::shrink (int newsize, bool leavehead)
{
  assert (newsize >= 0);

  if (newsize > m_size)
    {
      newsize = m_size;
    }
  CoeffInformation *tmp = NEW (CoeffInformation [newsize]);
  copyCoeffs (tmp, newsize, leavehead);

  DELETENOTNULLAR (m_coeffs);
  m_coeffs = tmp;
  m_size = newsize;
}

ImageInformation*
ImageInformation::subimage (int n, bool head)
{
  int newsize = n;
  ImageInformation *ret = NEW (ImageInformation (newsize));
  copyCoeffs (ret->m_coeffs, newsize, head);
  return ret;
}

void
ImageInformation::copyCoeffs (CoeffInformation *coeffs, int n, bool head)
{
  int offs = (head? 0: m_size - n);
  for (int i = 0; i < n; i++)
    {
      coeffs[i].set (m_coeffs[i+offs]);
    }
}

bool
ImageInformation::equals (ImageInformation &ii)
{
  int i;
  if (m_size != ii.size ())
    {
      return false;
    }
  for (i = 0; i < m_size && m_coeffs[i].equals (ii.at (i)); i++)
    {
    }
  return i == m_size;
}

void
ImageInformation::gensort (cipredicate isless)
{
  quicksort (0, m_size - 1, isless);
}


void
ImageInformation::ssort (void)
{
  quicksort (0, m_size - 1, valless);
}

void
ImageInformation::asort (void)
{
  quicksort (0, m_size - 1, avalless);
}

void
ImageInformation::psort (void)
{
  quicksort (0, m_size - 1, posless);
}
void
ImageInformation::yxsort (void)
{
  quicksort (0, m_size - 1, yxless);
}
void
ImageInformation::xysort (void)
{
  quicksort (0, m_size - 1, xyless);
}
void
ImageInformation::isort (void)
{
  quicksort (0, m_size - 1, idless);
}

void
ImageInformation::shuffle (void)
{
  for (int i = 0; i < m_size; i++)
    {
      swap (randint (0, m_size), randint (0, m_size/2));
      swap (randint (0, m_size), randint (0, m_size));
      swap (randint (0, m_size), randint (m_size/2, m_size));
      swap (randint (m_size/2, m_size), randint (0, m_size/2));
      swap (randint (m_size/2, m_size), randint (0, m_size));
      swap (randint (m_size/2, m_size), randint (m_size/2, m_size));
    }
}

bool
ImageInformation::ssorted (void) const
{
  int i;
  if (m_size < 2)
    {
      return true;
    }
  for (i = 0; i < m_size - 1 && m_coeffs[i].val () < m_coeffs[i+1].val (); i++)
    {
    }
  return i == m_size - 1;
}

bool
ImageInformation::asorted (void) const
{
  int i;
  if (m_size < 2)
    {
      return true;
    }
  for (i = 0; i < m_size - 1
         && fabs (m_coeffs[i].val ()) < fabs (m_coeffs[i+1].val ()); i++)
    {
    }
  return i == m_size - 1;
}

bool
ImageInformation::psorted (void) const
{
  int i;
  if (m_size < 2)
    {
      return true;
    }
  for (i = 0; i < m_size - 1
         && m_coeffs[i].xypos () < m_coeffs[i+1].xypos (); i++)
    {
    }
  return i == m_size - 1;
}

bool
ImageInformation::isorted (void) const
{
  int i;
  if (m_size < 2)
    {
      return true;
    }
  for (i = 0; i < m_size - 1
         && m_coeffs[i].id () < m_coeffs[i+1].id (); i++)
    {
    }
  return i == m_size - 1;
}

void
ImageInformation::swap (int pos1, int pos2)
{
  assert (pos1 >= 0);
  assert (pos2 >= 0);
  assert (pos1 < m_size);
  assert (pos2 < m_size);
  if (pos1 == pos2)
    {
      return;
    }
  CoeffInformation tmp (m_coeffs[pos1]);
  m_coeffs[pos1].set (m_coeffs[pos2]);
  m_coeffs[pos2].set (tmp);
}

/*
 * Advanced Quicksort, two-way partitioning, stable with equal
 * elements, according to Jon Bentley, "Programming Pearls", 2nd ed
 */
void
ImageInformation::quicksort (int from, int to, cipredicate isless)
{
  if (from >= to)
    {
      return;
    }
  CoeffInformation &t = m_coeffs[from];
  int pos = from;
  int split = to + 1;
  for (;;)
    {
      do
        {
          pos++;
        }
      while (pos <= to && (*isless) (&m_coeffs[pos], &t));

      do
        {
          split--;
        }
      while ((*isless) (&t, &m_coeffs[split]));

      if (pos > split)
        {
          break;
        }
      swap(pos, split);
  }
  swap(from, split);
  quicksort (from, split - 1, isless);
  quicksort (split + 1, to, isless);
}

/*
 * Random integers according to Jon Bentley, slightly changed
 * (always get values *less* than to, only positive numbers).
 */
int
ImageInformation::randint (int from, int to)
{
  return from + abs ((int)((RAND_MAX * rand () + rand ()) % (to - from)));
}

coeff
ImageInformation::amax (void)
{
  coeff max = 0.0;
  for (int i = 0; i < m_size; i++)
    {
      coeff tmp = fabs (m_coeffs[i].val ());
      if (tmp > max)
        {
          max = tmp;
        }
    }
  return max;
}

coeff
ImageInformation::smax (void)
{
  coeff max = -COEFF_MAX;
  for (int i = 0; i < m_size; i++)
    {
      if (m_coeffs[i].val () > max)
        {
          max = m_coeffs[i].val ();
        }
    }
  return max;
}

coeff
ImageInformation::amin (void)
{
  coeff min = COEFF_MAX;
  for (int i = 0; i < m_size; i++)
    {
      coeff tmp = fabs (m_coeffs[i].val ());
      if (tmp < min)
        {
          min = tmp;
        }
    }
  return min;
}

coeff
ImageInformation::smin (void)
{
  coeff min = COEFF_MAX;
  for (int i = 0; i < m_size; i++)
    {
      if (m_coeffs[i].val () < min)
        {
          min = m_coeffs[i].val ();
        }
    }
  return min;
}

#ifndef _WIN32_WCE
void
ImageInformation::dump (const char *delim, const char *file) const
{
  ostream *stream;
  if (file[0] == '\0')
    {
      stream = &cout;
    }
  else
    {
      stream = NEW (ofstream (file));
      if (stream->fail ())
        {
          throw ios_base::failure ("Error opening file for writing.");
        }
    }
  stream->setf (ios::fixed, ios::floatfield);
  for (int i = 0; i < m_size; i++)
    {
      *stream << "("  << m_coeffs[i].ypos ()
              << ", " << m_coeffs[i].xpos ()
              << ": " << std::setprecision (2) << std::setw (4)
              << m_coeffs[i].val () << ")" << delim;
    }
  stream->setf (FMT0, ios::floatfield);
  if (stream != &cout)
    {
      DELETE (stream);
    }
}
#endif

static bool
valless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->svcmp (*c2) < 0;
}

static bool
avalless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->avcmp (*c2) < 0;
}

static bool
posless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->xypos () < c2->xypos ();
}

static bool
yxless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->ypos () < c2->ypos ()
    || (c1->ypos () == c2->ypos () && c1->xpos () < c2->xpos ());
}

static bool
xyless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->xpos () < c2->xpos ()
    || (c1->xpos () == c2->xpos () && c1->ypos () < c2->ypos ());
}

static bool
idless (const CoeffInformation *c1, const CoeffInformation *c2)
{
  return c1->id () < c2->id ();
}

coeff
ImageInformation::aaverage (void) const
{
  coeff rc = 0;
  for (int i = 0; i < m_size; i++)
    {
      rc += fabs (m_coeffs[i].val ());
    }
  return rc / m_size;
}

coeff
ImageInformation::saverage (void) const
{
  coeff rc = 0;
  for (int i = 0; i < m_size; i++)
    {
      rc += m_coeffs[i].val ();
    }
  return rc / m_size;
}

double
ImageInformation::sqvariance (void) const
{
  double avg;
  double rc;

  avg = saverage ();
  avg *= avg;

  rc = 0.0;

  for (int i = 0; i < m_size; i++)
    {
      rc += m_coeffs[i].val () * m_coeffs[i].val ();
    }

  return (rc / m_size) - avg;
}

double
ImageInformation::variance (bool abs) const
{
  double avg;
  double rc;
  double tmp;

  avg = abs? aaverage () : saverage ();

  rc = 0.0;

  for (int i = 0; i < m_size; i++)
    {
      tmp = abs? fabs (m_coeffs[i].val ()) : m_coeffs[i].val ();
      tmp -= avg;
      rc += SQUARE (tmp);
    }

  return rc / m_size;
}


double
ImageInformation::sdeviation (bool abs) const
{
  return sqrt (variance (abs));
}


void
ImageInformation::resize (int size)
{
  int max = size < m_size? size: m_size;
  CoeffInformation *newCoeffs = NEW (CoeffInformation[size]);
  for (int i = 0; i < max; i++)
    {
      assert (i < size);
      newCoeffs[i].set (m_coeffs[i]);
    }

  DELETENOTNULLAR (m_coeffs);
  m_coeffs = newCoeffs;
  m_size = size;
}

int
ImageInformation::locate (int abs) const
{
  for (int i = 0; i < m_size; i++)
    {
      if (m_coeffs[i].xypos () == abs)
        {
          return i;
        }
    }
  return -1;
}

int
ImageInformation::locate (int y, int x) const
{
  for (int i = 0; i < m_size; i++)
    {
      if (m_coeffs[i].ypos () == y && m_coeffs[i].xpos () == x)
        {
          return i;
        }
    }
  return -1;
}

