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

#include "WImage/StillImage.hh"
#include "WImage/ColorImage.hh"
#ifndef _WIN32_WCE
#include "WImage/PgmReader.hh"
#include "WImage/PpmReader.hh"
#include "WImage/RawReader.hh"
#endif
#include "WImage/tools.h"
#ifdef PFI
#include "WImage/PfgReader.hh"
#include "WImage/PfcReader.hh"
#include "WImage/PfgWriter.hh"
#include "WImage/PfcWriter.hh"
#include <pfi.h>
#endif
#ifdef JPEG
#include "WImage/JpgReader.hh"
#include "WImage/JpgWriter.hh"
#endif
#ifndef _WIN32_WCE
#include "WImage/PgmWriter.hh"
#include "WImage/PpmWriter.hh"
#include "WImage/RawWriter.hh"
#endif
#include "WImage/debug.h"
#ifdef _WIN32_WCE
#undef DELETE
#endif
#include <stdexcept>
#include <cassert>
#ifndef _WIN32_WCE
#include <iostream>
#else
#include "WImage/miscdefs.h"
#endif

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


ColorImage::ColorImage (int colors, clrmodel cmodel, Image **images,
                        bool references, bool isMine)
  : ColorBuffer (images == NULL? 0: images[0]->size (), colors, cmodel)
{
  init (images, references, isMine);
}

ColorImage::ColorImage (int rows, int cols, int colors, clrmodel cmodel)
  : ColorBuffer (rows * cols, colors, cmodel)
{
  for (int i = 0; i < m_colors; i++)
    {
      m_images[i] = NEW (StillImage (rows, cols));
    }
}


ColorImage::~ColorImage (void)
{
}

void
ColorImage::init (Image **images, bool isReference, bool isMine)
{
  this->m_quality = 100;
  this->m_isReference = isReference;

  if (m_colors > 0)
    {
      for (int i = 0; i < m_colors; i++)
        {
          if (images == NULL)
            {
              m_images[i] = NEW (StillImage (0, 0));
            }
          else
            {
              m_images[i] = isReference? images[i] :
		(isMine? images[i]: images[i]->clone ());
            }
        }
    }
}

void
ColorImage::destroy (void)
{
  ColorBuffer::destroy ();
}

#ifndef _WIN32_WCE
void
ColorImage::read (char const * fname)
{
  FileName fn (fname);
  read (fname, fn.guess ());
}

void
ColorImage::read (char const * fname, filetype ftype)
{
  GreymapReader *gr = NULL;
  PixmapReader *pr = NULL;
  ImageArray <coeff> *coeffs = NEW (ImageArray <coeff> (0, 0));

  switch (ftype)
    {
    case fn_ppm:
      destroy ();
      ColorBuffer::init (3);
      ColorImage::init ();
      pr = NEW (PpmReader (fname, m_images));
      break;
#ifdef JPEG
    case fn_jpg:
      destroy ();
      ColorBuffer::init (3);
      ColorImage::init ();
      pr = NEW (JpgReader (fname, m_images));
      break;
#endif
    case fn_pgm:
      gr = NEW (PgmReader (fname, *coeffs));
      break;
#ifdef PFI
    case fn_pfi:
      {
        int rows, cols, colors, colormodel;
        if (pfi_properties_name ((char*)fname, &rows,
                                 &cols, &colors, &colormodel) < 0)
          {
	    DELETE (coeffs);
            throw invalid_argument ("read: corrupt PFI file.");
          }
        if (colors == 1)
          {
            gr = NEW (PfgReader (fname, *coeffs));
          }
        else
          {
            destroy ();
            ColorBuffer::init (colors);
            ColorImage::init ();
            pr = NEW (PfcReader (fname, m_images, colors));
          }
      }
      break;
#endif
    default:
      DELETE (coeffs);
      throw invalid_argument ("read: unknown file type.");
    }

  try
    {
      if (gr != NULL)
	{
	  /* this is pretty inefficient but not at all critical anyway */
	  gr->read ();
	  destroy ();
	  m_colors = 1;
	  m_images = NEW (Image* [m_colors]);
	  m_images[0] = NEW (StillImage (*coeffs));
	  DELETE (gr);
	  m_cmodel = cm_grey;
	}
      else
	{
	  pr->read ();
	  if (pr->channels () == 1)
	    {
	      // memory already released in the reader
	      for (int i = pr->channels (); i < m_colors; i++)
		{
		  m_images[i] = NULL;
		}
	      m_cmodel = cm_grey;
	      m_colors = pr->channels ();
	    }
	  else
	    {
	      m_cmodel = pr->colormodel ();
	    }
	  DELETE (pr);
	}
      DELETE (coeffs);
    }
  catch (...)
    {
      DELETE (coeffs);
      DELETENOTNULL (pr);
      DELETENOTNULL (gr);
      throw;
    }

  m_ysize = m_images[0]->rows ();
  m_xsize = m_images[0]->cols ();
  m_xysize = m_images[0]->size ();
  assert (m_cmodel != cm_unknown);
}

void
ColorImage::write (char const *fname, bool beautify)
{
  if (m_colors < 1)
    {
      throw invalid_argument ("write: empty image.");
    }

  FileName fn (fname);
  write (fname, fn.guess (), beautify);
}

void
ColorImage::write (char const *fname, filetype ftype, bool beautify)
{
  GreymapWriter *gw = NULL;
  PixmapWriter *pw = NULL;
  bool ispixel = true;
  ImageArray <coeff> *coeffs = NEW (ImageArray <coeff> (0, 0));

  switch (ftype)
    {
    case fn_ppm:
      if (m_colors != 3)
        {
          throw invalid_argument ("write: number of colors is not 3");
        }
      pw = NEW (PpmWriter (fname, m_images));
      break;
#ifdef JPEG
    case fn_jpg:
      pw = NEW (JpgWriter (fname, m_images, m_colors, m_quality));
      break;
#endif
    case fn_pgm:
      if (m_colors > 1)
        {
          throw invalid_argument ("write: color reduction "
                                  "is source-format-dependent.");
        }
      gw = NEW (PgmWriter (fname, *coeffs));
      break;
#ifdef PFI
    case fn_pfi:
      if (m_colors == 1)
        {
          gw = NEW (PfgWriter (fname, *coeffs));
        }
      else
        {
          pw = NEW (PfcWriter (fname, m_images, m_colors, m_cmodel));
        }
      ispixel = false;
      break;
#endif
    default:
      throw invalid_argument ("write: unknown file type.");
    }

  ColorImage *backup = NULL;
  if (beautify
    || (ispixel && (m_images[0]->anasteps () - m_images[0]->synsteps () > 0
                    || (this->smin () < 0) || (this->smax () > 255))))
    {
      backup = this->clone ();
      this->beautify ();
    }

  if (gw != NULL)
    {
      /* this is pretty inefficient but not at all critical anyway */
      coeffs->resize (m_images[0]->rows (), m_images[0]->cols ());
      for (int y = 0; y < m_images[0]->rows (); y++)
        {
          for (int x = 0; x < m_images[0]->cols (); x++)
            {
              coeffs->to (y, x, m_images[0]->at (y, x));
            }
        }
      gw->write ();
      DELETE (gw);
    }
  else
    {
      pw->write ();
      DELETE (pw);
    }

  if (backup != NULL)
    {
      for (int i = 0; i < m_colors; i++)
        {
          // We must not change the addresses here!
          m_images[i]->copy (backup->channel (i));
        }
      DELETE (backup);
    }
  DELETE (coeffs);
}
#endif

Image*
ColorImage::averageImage (void)
{
  Image *ret = m_images[0]->clone ();
  coeff avg;

  for (int i = 0; i < m_xysize; i++)
    {
      avg = 0;
      for (int j = 0; j < m_colors; j++)
        {
          avg += m_images[j]->at (i);
        }
      ret->to (i, avg / m_colors);
    }
  return ret;
}

ColorImage *
ColorImage::clone (void) const
{
  return NEW (ColorImage (m_colors, m_cmodel, m_images));
}

void
ColorImage::beautify (int yoffs, int xoffs, int ysize, int xsize)
{
  if (ysize > 0 && xsize > 0)
    {
      Image **tempchannels = NEW (Image* [m_colors]);
      for (int i = 0; i < m_colors; i++)
        {
          tempchannels[i] = m_images[i]->crop (yoffs, xoffs, ysize, xsize);
        }
      ColorImage templl (m_colors, m_cmodel, tempchannels);

      for (int i = 0; i < m_colors; i++)
        {
          DELETE (tempchannels[i]);
        }

      DELETE (tempchannels);

      coeff fill = (smax () - smin ()) / 2;

      for (int row = 0; row < ysize; row++)
        {
          for (int col = 0; col < xsize; col++)
            {
              for (int ch = 0; ch < m_colors; ch++)
                {
                  m_images[ch]->to (yoffs + col, xoffs + row, fill);
                }
            }
        }

      templl.valadjust ();
      valadjust ();

      for (int i = 0; i < m_colors; i++)
        {
          m_images[i]->paste (yoffs, xoffs, templl.channel (i));
        }
    }
  else
    {
      valadjust ();
    }
}

void
ColorImage::copy (ColorImage &img)
{
  if (img.colors () != colors ())
    {
      throw invalid_argument ("copy: number of colors do not match.");
    }
  for (int i = 0; i < colors (); i++)
    {
      m_images[i]->copy (img.channel (i));
    }
}

ColorImage *
ColorImage::crop (int yoffs, int xoffs, int ysize, int xsize) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < colors (); i++)
    {
      channels[i] = m_images[i]->crop (yoffs, xoffs, ysize, xsize);
    }
  ColorImage *ret = new ColorImage (colors (), colormodel (), channels,
				    false, true);
  DELETEAR (channels);
  return ret;
}

void
ColorImage::paste (int yoffs, int xoffs, ColorImage &img)
{
  if (img.colors () != colors ())
    {
      throw invalid_argument ("paste: number of colors do not match.");
    }
  for (int i = 0; i < colors (); i++)
    {
      m_images[i]->paste (yoffs, xoffs, img.channel (i));
    }
}

void
ColorImage::shift (int yoffs, int xoffs)
{
  for (int i = 0; i < colors (); i++)
    {
      m_images[i]->shift (yoffs, xoffs);
    }
}

void
ColorImage::fill (coeff *values, int fromY, int fromX, int toY, int toX)
{
  for (int i = 0; i < colors (); i++)
    {
      m_images[i]->fill (values[i], fromY, fromX, toY, toX);
    }
}

void
ColorImage::fill (int *values, int fromY, int fromX, int toY, int toX)
{
  for (int i = 0; i < colors (); i++)
    {
      m_images[i]->fill (values[i], fromY, fromX, toY, toX);
    }
}

ColorImage *
ColorImage::fitInto (int rows, int cols, int *fill, int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->fitInto (rows, cols,
					  fill == NULL? -1: fill[i], function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

ColorImage *
ColorImage::fitInto (int rows, int cols, bool mirrorExtend, int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->fitInto (rows, cols, mirrorExtend, function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

ColorImage *
ColorImage::fitInto (int rows, int cols, ExtendFunc extend, int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->fitInto (rows, cols, extend, function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

ColorImage *
ColorImage::fitInto (int rows, int cols, ResizeFillFunc *fillFunc,
			       int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->fitInto (rows, cols, fillFunc, function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

ColorImage *
ColorImage::scale (double factor, int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->scale (factor, function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

ColorImage *
ColorImage::scale (double yFactor, double xFactor, int function) const
{
  Image **channels = NEW (Image* [m_colors]);
  for (int i = 0; i < m_colors; i++)
    {
      channels[i] = m_images[i]->scale (yFactor, xFactor, function);
    }
  ColorImage *ret = NEW (ColorImage (m_colors, m_cmodel, channels,
				     false, true));
  DELETEAR (channels);
  return ret;
}

