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

//#undef DPRINTF
#include "Wave/WaveletTransform.hh"
#include "WImage/debug.h"
#include "WImage/tools.h"
#include <stdexcept>
#include <cassert>
#include <cmath>
#ifndef _WIN32_WCE
#include <iostream>
#else
#include "WImage/miscdefs.h"
#endif

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

void
WaveletTransform::sanity (void)
{
  if (m_image->rows () != m_buffer->rows ()
      || m_image->cols () != m_buffer->cols ())
    {
      m_buffer->resize (m_image->rows (), m_image->cols ());
    }
}

void
WaveletTransform::expandImage (void)
{
  int powerY = 0;
  int powerX = 0;

  if (!tools_powerOfTwo (m_image->rows (), powerY) ||
      m_image->rows () != m_image->cols ())
    {
      (void)tools_powerOfTwo (m_image->cols (), powerX);
      int newY, newX;
      m_rows = m_image->rows ();
      m_cols = m_image->cols ();
      newY = newX = TWOPOW (powerY > powerX? powerY: powerX);
      assert (newY >= m_rows);
      assert (newX >= m_cols);
      m_image->resize (newY, newX);
      sanity ();
    }
}


void
WaveletTransform::restoreImage (void)
{
  m_image->resize (m_rows, m_cols);
  sanity ();
}


void
WaveletTransform::analysis (int steps)
{
  //int power = 0;
  if (m_image->rows () != m_image->cols ())
    //|| !tools_powerOfTwo (m_image->rows (), power))
   // {
   //   throw invalid_argument ("analysis:"
   //                           " non square images are not supported.");
   // }
  sanity ();
  doanalysis (steps);
}

void
WaveletTransform::synthesis (int steps, int prevSteps)
{
  //if (m_image->rows () != m_image->cols ())
  //  {
  //    throw invalid_argument ("synthesis:"
  //                            " non square images are not supported.");
  //  }
  //int power = 0;
  //if (!tools_powerOfTwo (m_image->rows (), power))
  //  {
  //    throw invalid_argument ("synthesis:"
  //                            " sidelengths need to be powers of two.");
  //  }
  sanity ();
  dosynthesis (steps, prevSteps);
}


WaveletTransform::WaveletTransform (Image &img, FilterSet &fil)
{
  m_image = &img;
  m_filter = &fil;
  m_wavelet = NEW (Wavelet (fil));
  m_buffer = m_image->clone ();
  m_rows = m_image->rows ();
  m_cols = m_image->cols ();
}

WaveletTransform::~WaveletTransform (void)
{
  DELETE (m_wavelet);
  DELETE (m_buffer);
}


Image *
WaveletTransform::subband (area what, int steps)
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (what, steps, yoffs, xoffs, ysize, xsize);

  DPRINTF (("crop (%d, %d, %d, %d)\n", yoffs, xoffs, ysize, xsize));
  return m_image->crop (yoffs, xoffs, ysize, xsize);
}


Image *
WaveletTransform::highMax (int steps)
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  area what;
  Image *ret = subband (HL, steps);

  for (int a = (int)LH; a < (int)areaINVALID; a++)
    {
      what = (area)a;
      where (what, steps, yoffs, xoffs, ysize, xsize);
      for (int y = 0; y < ysize; y++)
	{
	  for (int x = 0; x < xsize; x++)
	    {
	      if (fabs (m_image->at (y + yoffs, x + xoffs))
		  > fabs (ret->at (y, x)))
		{
		  ret->to (y, x, m_image->at (y + yoffs, x + xoffs));
		}
	    }
	}
    }
  return ret;
}

void
WaveletTransform::import (Image &img, area what, int steps)
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (what, steps, yoffs, xoffs, ysize, xsize);
  if (ysize != img.rows () || xsize != img.cols ())
    {
      throw invalid_argument ("import: the image "
                              "dimensions don't match the subband size.");
    }
  DPRINTF (("import: area: %d, yoffs: %d, xoffs: %d\n",
            (int)what, yoffs, xoffs));
  m_image->paste (yoffs, xoffs, img);
}

void
WaveletTransform::fill (coeff value, area what, int steps)
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (what, steps, yoffs, xoffs, ysize, xsize);
  DPRINTF (("fill: area: %d, yoffs: %d, xoffs: %d, value: %f\n",
            (int)what, yoffs, xoffs, value));
  for (int row = 0; row < ysize; row++)
    {
      for (int col = 0; col < xsize; col++)
        {
          m_image->to (yoffs + row, xoffs + col, value);
        }
    }
}

void
WaveletTransform::where (area what, int subband,
                         int &yoffs, int &xoffs, int &ysize, int &xsize) const
{
  DPRINTF (("rows: %d, cols: %d\n", m_image->rows (), m_image->cols ()));
  if (subband < 0)
    {
      subband = this->steps ();
    }

  xoffs = yoffs = 0;

  DPRINTF (("subband: %d, anasteps: %d, synsteps: %d\n",
            subband, m_image->anasteps (), m_image->synsteps ()));

  ysize = m_image->rows () / TWOPOW (subband);
  xsize = m_image->cols () / TWOPOW (subband);

  if (what == LL)
    {
      ysize = (m_image->rows () + 1) / TWOPOW (subband);
      xsize = (m_image->cols () + 1) / TWOPOW (subband);
    }
  else if (what == HH)
    {
      yoffs = (m_image->rows () + 1) / TWOPOW (subband);
      xoffs = (m_image->cols () + 1) / TWOPOW (subband);
    }
  else if (what == HL)
    {
      ysize = (m_image->rows () + 1) / TWOPOW (subband);
      xoffs = (m_image->cols () + 1) / TWOPOW (subband);
    }
  else if (what == LH)
    {
      yoffs = (m_image->rows () + 1) / TWOPOW (subband);
      xsize = m_image->cols () / TWOPOW (subband);
    }

  DPRINTF (("area: %d, yoffs: %d, xoffs: %d, ysize: %d, xsize: %d\n",
            (int)what, yoffs, xoffs, ysize, xsize));
  assert (yoffs + ysize <= m_image->rows ());
  assert (xoffs + xsize <= m_image->cols ());
}

void
WaveletTransform::where (int ypos, int xpos, int &subband, area &channel,
                         int steps) const
{
  if (steps < 0)
    {
      steps = this->steps ();
    }
  int yoffs, xoffs, ysize, xsize;
  subband = steps;
  channel = LL;
  for (int i = 1; i <= steps && subband == steps && channel == LL; i++)
    {
      for (int j = 1; j <= 3 && subband == steps && channel == LL; j++)
        {
          where (areas[j], i, yoffs, xoffs, ysize, xsize);
          if (ypos >= yoffs && ypos < yoffs + ysize
              && xpos >= xoffs && xpos < xoffs + xsize)
            {
              subband = i;
              channel = areas[j];
            }
        }
    }
}

void
WaveletTransform::mapPosition (int yOld, int xOld, int toSubband,
                               area toChannel, int &yNew, int &xNew,
                               int &sizeFactor, int steps) const
{
  int fromSubband = -1;
  area fromChannel = areaINVALID;
  int y1 = 0, x1 = 0, h1 = 0, w1 = 0;
  int y2 = 0, x2 = 0, h2 = 0, w2 = 0;

  where (yOld, xOld, fromSubband, fromChannel, steps);
  where (fromChannel, fromSubband, y1, x1, h1, w1);
  where (toChannel, toSubband, y2, x2, h2, w2);

  sizeFactor = h2 / h1;

  /* calculate the destination (mapped) coordinates from their relative
   * channel position and the two channels' coordinates... */
  yNew = (int)(0.5f + (int)((float)(yOld - y1) / (float)h1 * h2) + y2);
  xNew = (int)(0.5f + (int)((float)(xOld - x1) / (float)w1 * w2) + x2);
  assert (yNew >= y2 && yNew < y2 + h2);
  assert (xNew >= x2 && xNew < x2 + w2);
}

coeff
WaveletTransform::ratio (int subband1, int subband2)
{
  return pow (4.0, (double)(subband2 - subband1));
}

int
WaveletTransform::getSubband (int ypos, int xpos, int steps)
{
  area channel;
  int subband;
  where (ypos, xpos, subband, channel, steps);
  return subband;
}

area
WaveletTransform::getArea (int ypos, int xpos, int steps)
{
  area channel;
  int subband;
  where (ypos, xpos, subband, channel, steps);
  return channel;
}

coeff
WaveletTransform::saverage (int subband, area channel) const
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (channel, subband, yoffs, xoffs, ysize, xsize);
  return m_image->saverage (yoffs, xoffs, yoffs + ysize, xoffs + xsize);
}


coeff
WaveletTransform::aaverage (int subband, area channel) const
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (channel, subband, yoffs, xoffs, ysize, xsize);
  return m_image->aaverage (yoffs, xoffs, yoffs + ysize, xoffs + xsize);
}


coeff
WaveletTransform::sqvariance (int subband, area channel) const
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (channel, subband, yoffs, xoffs, ysize, xsize);
  return m_image->sqvariance (yoffs, xoffs, yoffs + ysize, xoffs + xsize);
}

coeff
WaveletTransform::variance (int subband, area channel, bool abs) const
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (channel, subband, yoffs, xoffs, ysize, xsize);
  return m_image->variance (yoffs, xoffs, yoffs + ysize, xoffs + xsize, abs);
}


coeff
WaveletTransform::sdeviation (int subband, area channel, bool abs) const
{
  int yoffs = 0, xoffs = 0, ysize = 0, xsize = 0;
  where (channel, subband, yoffs, xoffs, ysize, xsize);
  return m_image->sdeviation (yoffs, xoffs, yoffs + ysize, xoffs + xsize, abs);
}

