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

#ifndef VIDEO_ARRAY_CC__
#define VIDEO_ARRAY_CC__

#include "WImage/debug.h"
#include "WImage/VideoArray.hh"
#ifndef _WIN32_WCE
# include <iostream>
#else
# undef DELETE
#endif
#include <stdexcept>
#include <cassert>
#include "WImage/miscdefs.h"

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

template <class T>
VideoArray<T>::VideoArray (int rows, int cols, int frames) 
{
  this->m_rows = NULL;
  this->m_ar = NULL;
  this->m_ysize = rows;
  this->m_xsize = cols;
  m_frames = frames;
  this->m_xysize = rows*cols;
  this->m_xyzsize = m_frames * this->m_xysize;
  m_current = 0;
  m_offset = 0;
  if (this->m_xyzsize > 0)
    {
      this->m_ar = NEW (T[this->m_xyzsize]);
    }
  for (int i = 0; i < this->m_xyzsize; i++)
    {
      this->m_ar[i] = (T)0;
    }

  this->updateRowsArray ();
}

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

template <class T> void 
VideoArray<T>::current (int frame) 
{ 
  if (frame < 0 || frame >= m_frames)
    {
      throw std::invalid_argument ("current: new frame out of bounds.");
    }
  m_current = frame; 
  m_offset = m_current * this->m_xysize;
}

template <class T> bool 
VideoArray<T>::epsilons (ImageArray<T> &ia, T epsilon) const
{
  VideoArray<T> *va = dynamic_cast < VideoArray < T >* > (&ia);
  if (va != NULL)
    {
      return epsilonsVideo (*va, epsilon);
    }
  return ImageArray<T>::epsilons (ia, epsilon);
}

template <class T> bool 
VideoArray<T>::epsilonsFrames (int f1, int f2, T epsilon) const
{
  int o1 = f1 * this->m_xysize;
  int o2 = f2 * this->m_xysize;
  
  if (f1 < 0 || f1 >= m_frames || f2 < 0 || f2 >= m_frames)
    {
      throw std::invalid_argument ("epsilonsFrames: invalid frames.");
    }
  for (int i = 0; i < this->m_xysize; i++)
    {
      if (this->m_ar[o1 + i] > this->m_ar[o2 + i])
        {
          if (this->m_ar[o1 + i] - this->m_ar[o2 + i] > epsilon)
            {
              return false;
            }
        }
      else
        {
          if (this->m_ar[o2 + i] - this->m_ar[o1 + i] > epsilon)
            {
              return false;
            }
        }
    }
  return true;
}

template <class T> bool 
VideoArray<T>::equalsFrames (int f1, int f2) const
{
  return epsilonsFrames (f1, f2, (T)0);
}

template <class T> bool 
VideoArray<T>::epsilonsVideo (VideoArray<T> &va, T epsilon) const
{
  if (!(va.frames () == this->frames ()
        && va.rows () == this->rows () 
        && va.cols () == this->cols ()
        && va.size () == this->size ()))
    {
      return false;
    }

  for (int i = 0; i < m_xyzsize; i++)
    {
      if (va.m_ar[i] > this->m_ar[i])
        {
          if (va.m_ar[i] - this->m_ar[i] > epsilon)
            {
              return false;
            }
        }
      else
        {
          if (this->m_ar[i] - va.m_ar[i] > epsilon)
            {
              return false;
            }
        }
    }
  return true;
}

template <class T>void 
VideoArray<T>::resize (int rows, int cols)
{
  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.");
    }

  int size = rows*cols;
  int ymin = MIN (rows, this->m_ysize);
  int xmin = MIN (cols, this->m_xsize);
  int x = 0, y = 0;

  T *tmp = NEW (T[m_frames * size]);

  for (int frame = 0; frame < m_frames; frame++)
    {
      for (y = 0; y < ymin; y++)
        {
          int oldoffs = this->m_xysize * frame + this->m_xsize * y;
          int newoffs = size * frame + cols * y;
          for (x = 0; x < xmin; x++)
            {
              assert (newoffs + x < size * m_frames);
              assert (oldoffs + x < this->m_xysize * m_frames);
              tmp[newoffs + x] = this->m_ar[oldoffs + x];
            }
          for (; x < cols; x++)
            {
              assert (newoffs + x < size * m_frames);
              tmp[newoffs + x] = (T)0;
            }
        }
  
      for (; y < rows; y++)
        {
          int newoffs = size * frame + cols * y;
          for (x = 0; x < cols; x++)
            {
              assert (newoffs + x < size * m_frames);
              tmp[newoffs + x] = (T)0;
            }
        }
    }
  

  DELETEAR (this->m_ar);
  this->m_ar = tmp;

  this->m_ysize = rows;
  this->m_xsize = cols;
  this->m_xysize = size;
  this->m_xyzsize = size * m_frames;
  this->updateRowsArray ();
  // need to recalculate the offset here!
  current (m_current);
}

template <class T>void 
VideoArray<T>::reframe (int frames, bool keephead)
{
  if (frames == m_frames)
    {
      return;
    }
  if (frames <= 0)
    {
      throw std::invalid_argument ("reframe: invalid number of frames.");
    }
  int from = 0;
  int to = m_frames;
  
  if (frames < m_frames)
    {
      if (keephead)
        {
          to = frames;
        }
      else
        {
          from = m_frames - frames;
        }
    }

  int sf;
  int df;

  T *ar = NEW (T[frames * this->m_xysize]);
  
  for (df = 0, sf = from; sf < to; df++, sf++)
    {
      int doffs = df * this->m_xysize;
      int soffs = sf * this->m_xysize;
      
      for (int i = 0; i < this->m_xysize; i++)
        {
          ar[doffs + i] = this->m_ar[soffs + i];
        }
    }

  if (frames > m_frames)
    {
      for (int frame = m_frames; frame < frames; frame++)
        {
          int offs = frame * this->m_xysize;
          for (int i = 0; i < this->m_xysize; i++)
            {
              assert (offs + i < frames * this->m_xysize);
              ar[offs + i] = (T)0;
            }
        }
    }
  
  DELETE (this->m_ar);
  this->m_ar = ar;
  m_frames = frames;
  this->m_xyzsize = this->m_xysize * m_frames;
  if (m_current >= m_frames)
    {
      m_current = m_frames - 1;
    }
}

template <class T>void 
VideoArray<T>::import (int rows, int cols, T *array)
{
  throw std::invalid_argument ("import: "
                               "this method cannot be used on videos.");
  
}

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

template <class T>VideoArray<T> *
VideoArray<T>::cloneVideo (void) const
{
  VideoArray<T> *clone = NEW (VideoArray (this->m_ysize, this->m_xsize, 
																					m_frames));
  assert (clone->m_xyzsize == this->m_xyzsize);
  for (int frame = 0; frame < m_frames; frame++)
    {
      int offset = frame * this->m_xysize;
      for (int i= 0; i < this->m_xysize; i++)
        {
          assert (offset + i < this->m_xyzsize);
          clone->m_ar[offset + i] = this->m_ar[offset + i];
        }
    }
  return clone;
}

template <class T>ImageArray<T> *
VideoArray<T>::clone (void) const
{
  ImageArray<T> *clone = NEW (ImageArray <T> (this->m_ysize, this->m_xsize));
  for (int i= 0; i < this->m_xysize; i++)
    {
      clone->to (i, this->at (i));
    }
  return clone;
}

template <class T>void
VideoArray<T>::copy (ImageArray<T> &ia) 
{
  VideoArray<T> *src = dynamic_cast < VideoArray < T >* > (&ia);
  if (src == NULL)
    {
      throw std::invalid_argument ("copy: Other object is not a VideoArray.");
    }
  if (src->frames () != this->frames () 
      || src->rows () != this->rows () 
      || src->cols () != this->cols ())
    {
      this->m_ysize = src->rows ();
      this->m_xsize = src->cols ();
      this->m_xysize = this->m_ysize * this->m_xsize;
      this->m_xyzsize = this->m_ysize * this->m_xsize * m_frames;
      m_frames = src->frames ();
      m_current = 0;
      m_offset = 0;
      DELETE (this->m_ar);
      if (this->m_xyzsize > 0)
        {
          this->m_ar = NEW (T[this->m_xyzsize]);
        }
      this->updateRowsArray ();
    }
  int scur = src->current ();
  for (int frame = 0; frame < m_frames; frame++)
    {
      current (frame);
      src->current (frame);
      for (int i = 0; i < src->size (); i++)
        {
          to (i, src->at (i));
        }
    }
  src->current (scur);
  current (scur);
}


#endif /* VIDEO_ARRAY_CC__ */
