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

#ifndef IMAGE_ARRAY_CC__
#define IMAGE_ARRAY_CC__

#include "WImage/debug.h"
#include "WImage/ImageArray.hh"
#ifndef _WIN32_WCE
# include <iostream>
#else
# undef DELETE
#endif
#include <stdexcept>
#include <cassert>
#include <climits>
#include <cstdio>
#include "WImage/tools.h"

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


template <class T>
ImageArray<T>::ImageArray (void)
{
  m_ar = NULL;
  m_rows = NULL;
  m_ysize = 0;
  m_xsize = 0;
  m_xysize = 0;
}

template <class T>
ImageArray<T>::ImageArray (int rows, int cols)
{
  m_rows = NULL;
  m_ar = NULL;
  m_ysize = rows;
  m_xsize = cols;
  m_xysize = rows*cols;
  if (m_xysize > 0)
    {
      m_ar = NEW (T[rows*cols]);
    }
  for (int i = 0; i < m_xysize; i++)
    {
      m_ar[i] = (T)0;
    }
  updateRowsArray ();
}

template <class T>
ImageArray<T>::~ImageArray (void)
{
  DELETENOTNULLAR (m_ar);
  DELETENOTNULLAR (m_rows);
}

template <class T>void
ImageArray<T>::resize (int rows, int cols)
{
  static const int maxSize = INT_MAX / 10;
  if (rows < 0 || cols < 0)
    {
      throw std::invalid_argument ("resize: rows and cols cannot "
                                   "have negative values.");
    }
  if (rows * cols == 0)
    {
      throw std::invalid_argument ("resize: "
                                   "array size cannot be zero.");
    }
  if (INT_MAX / rows < cols || rows * cols > maxSize)
    {
      char buf[512];
      snprintf (buf, sizeof buf - 1,
		"resize: array size would be larger than "
	       "max allowed size of %d elements.", maxSize);
      throw std::invalid_argument (buf);
    }
  int size = rows * cols;
  int ymin = MIN (rows, m_ysize);
  int xmin = MIN (cols, m_xsize);
  int oldXySize = m_xysize;
  int x = 0, y = 0;

  //std::cout << "rows: " << rows << std::endl;
  //std::cout << "cols: " << cols << std::endl;
  T *tmp = NEW (T[size]);
  //std::cout << "size: " << size << std::endl;

  m_ysize = rows;
  m_xsize = cols;
  m_xysize = size;

  int *oldRows = m_rows;
  m_rows = NULL;
  updateRowsArray ();

  //DPRINTF (("resize (%d, %d)\n", m_ysize, m_xsize));

  for (y = 0; y < ymin; y++)
    {
      int y1offs = m_rows[y];
      int y2offs = oldRows[y];
      for (x = 0; x < xmin; x++)
        {
          //DPRINTF (("tmp[%d+%d] = m_ar[%d+%d]: %d\n",
          //          y1offs, x, y2offs, x, (int)m_ar[y2offs]));
	  assert (y1offs + x >= 0);
	  assert (y2offs + x >= 0);
	  assert (y1offs + x < size);
	  assert (y2offs + x < oldXySize);
          tmp[y1offs + x] = m_ar[y2offs + x];
        }
      for (; x < m_xsize; x++)
        {
          //DPRINTF (("%d < %d; tmp[%d+%d] = (T)0\n", x, m_xsize, y1offs, x));
          tmp[y1offs + x] = (T)0;
        }
    }

  for (; y < m_ysize; y++)
    {
      int y1offs = m_rows[y];
      for (x = 0; x < m_xsize; x++)
        {
          //DPRINTF (("%d < %d; tmp[%d+%d] = (T)0\n", y, rows, y1offs, x));
          tmp[y1offs + x] = (T)0;
        }
    }

  DELETEAR (m_ar);
  m_ar = tmp;
  DELETEAR (oldRows);
}

template <class T>void
ImageArray<T>::import (int rows, int cols, T *array)
{
  DELETEAR (m_ar);
  m_ar = array;
  m_ysize = rows;
  m_xsize = cols;
  m_xysize = cols*rows;
  updateRowsArray ();
}

template <class T>ImageArray<T> *
ImageArray<T>::clone (void) const
{
  ImageArray<T> *clone = NEW (ImageArray (m_ysize, m_xsize));
  for (int row = 0; row < m_ysize; row++)
    {
      for (int col = 0; col < m_xsize; col++)
        {
          clone->to (row, col, m_ar[m_rows[row] + col]);
        }
    }
  return clone;
}

template <class T>void
ImageArray<T>::copy (ImageArray<T> &ia)
{
  if (ia.size () == 0)
    {
      DELETE (m_ar);
      m_xsize = ia.cols ();
      m_ysize = ia.rows ();
      m_xysize = ia.size ();
      updateRowsArray ();
    }
  else
    {
      resize (ia.rows (), ia.cols ());
      for (int i = 0; i < ia.size (); i++)
        {
          to (i, ia.at (i));
        }
    }
}

template <class T>bool
ImageArray<T>::epsilons (ImageArray<T> &ia, T epsilon) const
{
  if (!(ia.rows () == this->rows ()
        && ia.cols () == this->cols ()
        && ia.size () == this->size ()))
    {
      //cout << "dimensions failed." << endl;
      return false;
    }
  for (int row = 0; row < ia.rows (); row++)
    {
      for (int col = 0; col < ia.cols (); col++)
        {
	  if (!tools_epsilons (ia.at (row, col), this->at (row, col), epsilon))
	    {
	      /*std::cout << "at " << row << " / " << col
			<< ": "
			<< "this: " << this->at (row, col)
			<< ", that: " << ia.at (row, col)
			<< ", diff: " << this->at (row, col) - ia.at (row, col)
			<< std::endl;*/
	      return false;
	    }
        }
    }
  return true;
}

template <class T>T
ImageArray<T>::smax (int fromY, int fromX, int toY, int toX) const
{
  T max;
  T cur;
  int maxY = (toY < 0? rows (): toY);
  int maxX = (toX < 0? cols (): toX);

  max = at (fromY, fromX);

  for (int y = fromY; y < maxY; y++)
    {
      for (int x = fromX; x < maxX; x++)
        {
          cur = at (y, x);
          if (cur > max)
            {
              max = cur;
            }
        }
    }
  return max;
}

template <class T>T
ImageArray<T>::smin (int fromY, int fromX, int toY, int toX) const
{
  T min;
  T cur;
  int maxY = (toY < 0? rows (): toY);
  int maxX = (toX < 0? cols (): toX);

  min = at (fromY, fromX);

  for (int y = fromY; y < maxY; y++)
    {
      for (int x = fromX; x < maxX; x++)
        {
          cur = at (y, x);
          if (cur < min)
            {
              min = cur;
            }
        }
    }
  return min;
}

template <class T>T
ImageArray<T>::amax (int fromY, int fromX, int toY, int toX) const
{
  T max;
  T cur;
  int maxY = (toY < 0? rows (): toY);
  int maxX = (toX < 0? cols (): toX);

  max = at (fromY, fromX);
  max = (max < 0? -1*max: max);

  for (int y = fromY; y < maxY; y++)
    {
      for (int x = fromX; x < maxX; x++)
        {
          cur = at (y, x);
          cur = (cur < 0? -1*cur: cur);
          if (cur > max)
            {
              max = cur;
            }
        }
    }
  return max;
}

template <class T>T
ImageArray<T>::amin (int fromY, int fromX, int toY, int toX) const
{
  T min;
  T cur;
  int maxY = (toY < 0? rows (): toY);
  int maxX = (toX < 0? cols (): toX);

  min = at (fromY, fromX);
  min = (min < 0? -1*min : min);

  for (int y = fromY; y < maxY; y++)
    {
      for (int x = fromX; x < maxX; x++)
        {
          cur = at (y, x);
          cur = (cur < 0? -1*cur: cur);
          if (cur < min)
            {
              min = cur;
            }
        }
    }
  return min;
}

template <class T>T
ImageArray<T>::saverage (int fromY, int fromX, int toY, int toX) const
{
  T ret = 0;
  int nVals = 0;
  int lower = (toY < 0)? m_ysize: toY;
  int right = (toX < 0)? m_xsize: toX;

  for (int y = (fromY < 0? 0: fromY); y < lower && y < m_ysize; y++)
    {
      for (int x = (fromX < 0? 0: fromX); x < right && x < m_xsize; x++)
        {
          ++nVals;
          ret += at (y, x);
        }
    }

  return (nVals > 0)? ret / static_cast < T > (nVals): 0;
}

template <class T>T
ImageArray<T>::aaverage (int fromY, int fromX, int toY, int toX) const
{
  T ret = 0;
  int nVals = 0;
  int lower = (toY < 0)? m_ysize: toY;
  int right = (toX < 0)? m_xsize: toX;

  for (int y = (fromY < 0? 0: fromY); y < lower && y < m_ysize; y++)
    {
      for (int x = (fromX < 0? 0: fromX); x < right && x < m_xsize; x++)
        {
          ++nVals;
          T tmp = at (y, x);
          ret += tmp < 0? -tmp: tmp;
        }
    }

  return (nVals > 0)? ret / static_cast < T > (nVals): 0;
}



template <class T>void
ImageArray<T>::updateRowsArray (void)
{
  if (m_rows != NULL)
    {
      DELETE (m_rows);
    }
  if (m_ysize > 0)
    {
      m_rows = NEW (int [m_ysize]);
    }
  for (int i = 0; i < m_ysize; i++)
    {
      m_rows[i] = m_xsize * i;
    }
}

#endif /* IMAGE_ARRAY_CC__ */
