/* AbiSource Program Utilities
 * Copyright (C) 2002 Francis James Franklin
 * Copyright (C) 2002 AbiSource, Inc.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */


#include "ut_canvas.h"

#include "ut_canvas_hset.h"
#include "ut_canvas_image.h"
#include "ut_canvas_map.h"
#include "ut_canvas_png.h"
#include "ut_canvas_polygon.h"

UT_Canvas::UT_Canvas () :
  m_CanvasMode(cm_Canvas),
  m_left(0),
  m_right(0),
  m_top(0),
  m_bottom(0),
  m_BlendMode(bm_SrcCpy),
  m_HSet(128),
  m_empty(true)
{
  //
}

UT_Canvas::~UT_Canvas ()
{
  UT_VECTOR_PURGEALL (CanvasHSet *, m_HSet);
}

bool UT_Canvas::setAlpha (UT_sint32 x, UT_sint32 y, unsigned char a)
{
  xSet (x);
  ySet (y);

  bool create = (a ? true : false);
  CanvasHSet * hset = getHSet (y, create);
  if (hset)
    {
      hset->setBlend (m_BlendMode);
      return hset->setAlpha (x, a);
    }
  return (create ? false : true);
}

bool UT_Canvas::setColor (UT_sint32 x, UT_sint32 y, const UT_RGBAColor & rgba)
{
  xSet (x);
  ySet (y);

  bool create = ((rgba.m_r || rgba.m_g || rgba.m_b || rgba.m_a) ? true : false);
  CanvasHSet * hset = getHSet (y, create);
  if (hset)
    {
      hset->setBlend (m_BlendMode);
      return hset->setColor (x, rgba);
    }
  return (create ? false : true);
}

bool UT_Canvas::setAlpha (UT_sint32 offset_x, UT_sint32 offset_y, unsigned char a,
			  UT_uint32 width, UT_uint32 height, bool force_pixel)
{
  xSet (offset_x);
  xSet (offset_x + width - 1);
  ySet (offset_y);
  ySet (offset_y + height - 1);

  bool create = ((force_pixel || a) ? true : false);
  bool success = true;
  for (UT_sint32 y = offset_y; y < offset_y + height; y++)
    {
      CanvasHSet * hset = getHSet (y, create);
      if (hset)
	{
	  hset->setBlend (m_BlendMode);
	  if (!hset->setAlpha (offset_x, a, width, force_pixel)) success = false;
	}
      else if (create) success = false;
    }
  return success;
}

bool UT_Canvas::setColor (UT_sint32 offset_x, UT_sint32 offset_y, const UT_RGBAColor & rgba, 
			  UT_uint32 width, UT_uint32 height, bool force_pixel)
{
  xSet (offset_x);
  xSet (offset_x + width - 1);
  ySet (offset_y);
  ySet (offset_y + height - 1);

  bool create = ((force_pixel || (rgba.m_r || rgba.m_g || rgba.m_b || rgba.m_a)) ? true : false);
  bool success = true;
  for (UT_sint32 y = offset_y; y < offset_y + height; y++)
    {
      CanvasHSet * hset = getHSet (y, create);
      if (hset)
	{
	  hset->setBlend (m_BlendMode);
	  if (!hset->setColor (offset_x, rgba, width, force_pixel)) success = false;
	}
      else if (create) success = false;
    }
  return success;
}

bool UT_Canvas::setAlpha (UT_sint32 x, UT_sint32 y, const CanvasHSet * src, UT_sint32 src_x, UT_uint32 length)
{
  if (src == 0) return false;

  xSet (x);
  xSet (x + length - 1);
  ySet (y);

  CanvasHSet * hset = getHSet (y, true);
  if (hset)
    {
      hset->setBlend (m_BlendMode);
      return src->copyAlpha (hset, x, src_x, length);
    }
  return false;
}

bool UT_Canvas::setColor (UT_sint32 x, UT_sint32 y, const CanvasHSet * src, UT_sint32 src_x, UT_uint32 length)
{
  if (src == 0) return false;

  xSet (x);
  xSet (x + length - 1);
  ySet (y);

  CanvasHSet * hset = getHSet (y, true);
  if (hset)
    {
      hset->setBlend (m_BlendMode);
      return src->copyColor (hset, x, src_x, length);
    }
  return false;
}

unsigned char UT_Canvas::getAlpha (UT_sint32 x, UT_sint32 y) const
{
  CanvasHSet * hset = getHSet (y);
  if (hset)
    return hset->getAlpha (x);
  return 0;
}

const UT_RGBAColor UT_Canvas::getColor (UT_sint32 x, UT_sint32 y) const
{
  CanvasHSet * hset = getHSet (y);
  if (hset)
    return hset->getColor (x);
  return UT_RGBAColor();
}

bool UT_Canvas::copyAlpha (UT_Canvas * dest, UT_sint32 dest_x, UT_sint32 dest_y,
			   UT_sint32 src_x, UT_sint32 src_y, UT_uint32 width, UT_uint32 height) const
{
  if (dest == 0) return false;

  bool success = true;
  for (UT_sint32 y = src_y; y < src_y + height; y++)
    {
      CanvasHSet * hset = getHSet (y);
      if (hset)
	if (!dest->setAlpha (dest_x, dest_y + y - src_y, hset, src_x, width)) success = false;
    }
  return success;
}

bool UT_Canvas::copyColor (UT_Canvas * dest, UT_sint32 dest_x, UT_sint32 dest_y,
			   UT_sint32 src_x, UT_sint32 src_y, UT_uint32 width, UT_uint32 height) const
{
  if (dest == 0) return false;

  bool success = true;
  for (UT_sint32 y = src_y; y < src_y + height; y++)
    {
      CanvasHSet * hset = getHSet (y);
      if (hset)
	if (!dest->setColor (dest_x, dest_y + y - src_y, hset, src_x, width)) success = false;
    }
  return success;
}

bool UT_Canvas::swapColor (UT_Canvas * dest, UT_sint32 dest_x, UT_sint32 dest_y,
			   UT_sint32 src_x, UT_sint32 src_y, UT_uint32 width, UT_uint32 height)
{
  if (dest == 0) return false;

  bool success = true;
  for (UT_sint32 y = src_y; y < src_y + height; y++)
    {
      CanvasHSet * hset = getHSet (y);
      if (hset)
	if (!dest->swapColor (dest_x, dest_y + y - src_y, hset, src_x, width)) success = false;
    }
  return success;
}

bool UT_Canvas::swapColor (UT_sint32 x, UT_sint32 y, CanvasHSet * src, UT_sint32 src_x, UT_uint32 length)
{
  if (src == 0) return false;

  xSet (x);
  xSet (x + length - 1);
  ySet (y);

  CanvasHSet * hset = getHSet (y, true);
  if (hset)
    {
      return src->swapColor (hset, x, src_x, length);
    }
  return false;
}

CanvasHSet * UT_Canvas::getHSet (UT_sint32 offset_y, bool create)
{
  UT_uint32 posNew = 0;
  UT_uint32 count = m_HSet.getItemCount ();

  if (count == 1)
    {
      CanvasHSet * only = (CanvasHSet *) m_HSet.getFirstItem ();
      if (only->yOffset () == offset_y) return only;

      if (only->yOffset () < offset_y) posNew = 1;
    }
  else if (count > 1)
    {
      CanvasHSet * setFound = 0;
      do
	{
	  count--;

	  CanvasHSet * hset = (CanvasHSet *) m_HSet.getNthItem (count);
	  if (hset->yOffset () == offset_y)
	    {
	      setFound = hset;
	      break;
	    }
	  if (hset->yOffset () < offset_y)
	    {
	      posNew = count + 1;
	      break;
	    }
	}
      while (count);
      if (setFound) return setFound;
    }

  if (!create) return 0;

  CanvasHSet * setNew = new CanvasHSet(offset_y);
  if (m_HSet.insertItemAt ((void *) setNew, posNew) < 0)
    {
      delete setNew;
      return 0;
    }
  m_empty = false;
  return setNew;
}