#include <colors.h>
#include <image.h>

///////////////////////////////////////////////////////////
// TImage
///////////////////////////////////////////////////////////


// Certified 99%
// @doc EXTERNAL

// @mfunc Setta l'immagine e le sue dimensioni
//
// @rdesc Ritorna l'immagine stessa
XVT_IMAGE TImage::set(
  XVT_IMAGE i) // @parm Immagine da settare

  // @comm L'immagine precedente viene cancellata quando viene settata una nuova
{
  if (_image) 
    xvt_image_destroy(_image);
  _image = i;
  if (i)
  {
    _src.left = _src.top = 0;
		short w, h;
    xvt_image_get_dimensions(i, &w, &h);
		_src.right = w;
		_src.bottom = h;
    _dst = _src;
  } 
  return _image;
}

// Certified 100%
// @doc EXTERNAL

// @mfunc Legge l'immagine dal file
XVT_IMAGE TImage::load(
  const char* n) // @parm Nome del file contenente l'immagine
{               
  XVT_IMAGE i = xvt_image_read((char*)n);
  if (i != NULL) 
    set(i);
  return i;  
}

// Certified 100%
XVT_IMAGE TImage::load(short id)
{                         
  return set(xvt_res_get_image(id));
}

// Certified 100%
TImage::TImage(const char* n) : _image(NULL)
{
  if (n && *n) 
    load(n);
}

// Certified 100%
TImage::TImage(short id) : _image(NULL)
{
  if (id > 0) load(id);
}

// Certified 90%
TImage::TImage(const TImage& im, short w, short h) : _image(NULL)
{ 
  const XVT_IMAGE_FORMAT fmt = xvt_image_get_format(im._image);
  if (w < 0 || h < 0)
  {
    short iw, ih;
    xvt_image_get_dimensions(im._image, &iw, &ih);
    if (w < 0) w = iw;
    if (h < 0) h = ih;
  }
  set(xvt_image_create(fmt, w, h, 0L));
  
  if (ok())
  { 
#ifndef XVAGA
    if (fmt == XVT_IMAGE_CL8)
    {
      const short colors = xvt_image_get_ncolors(im._image);
      xvt_image_set_ncolors(_image, colors);
      for (short c = 0; c < colors; c++) 
        xvt_image_set_clut(_image, c, xvt_image_get_clut((XVT_IMAGE)im._image, c));
    }    
#endif
    xvt_image_transfer(_image, (XVT_IMAGE)im._image, &_src, (RCT*)&im._src);
  }  
}

// Certified 90%
TImage::TImage(short w, short h, XVT_IMAGE_FORMAT fmt) : _image(NULL)
{ 
  set(xvt_image_create(fmt, w, h, 0L));
}

// Certified 100%
TImage::~TImage()
{     
  if (_image != NULL)                    
    xvt_image_destroy(_image);
}

// Certified 100%
// @doc EXTERNAL

// @mfunc Permette di settare la posizione della figura
void TImage::set_pos(
  int x, // @parm Coordinata x dell'immagine da settare
  int y) // @parm Coordinata y dell'immagine da settare
  
  // @comm Permette di aggiornare il mebro <p _dst> sommandogli i valori                 
  //                     passati con <p x> e <p y>
{
  _dst = _src;
  xvt_rect_offset(&_dst, x, y);
}               

// Certified 100%
void TImage::draw(WINDOW w) const
{   
  xvt_dwin_draw_image(w, _image, (RCT*)&_dst, (RCT*)&_src);
}

// Certified 100%
// @doc EXTERNAL

// @mfunc Permette di gestire il disegno dell'immagine sullo schermo
void TImage::draw(
  WINDOW w,    // @parm Immagine da disegnare
  int x,       // @parm Coordinata x in cui disegnare l'immagine
  int y) const // @parm Coordinata y in cui disegnare l'immagine
  // @parm RCT& | _src | Rettangolo contenente l'immagine da disegnare
  // @parm RCT& | _dst | Rettangolo in cui disegnare l'immagine

  // @syntax void draw(WINDOW w);
  // @syntax void draw(WINDOW w, int x, int y);
  // @syntax void draw(WINDOW w, const RCT& dst);
  // @syntax void draw(WINDOW w, const RCT& dst, const RCT& src);

  // @comm Nel caso utilizzo l'ultima sintassi e' possibile disegnare solo una parte
  //                     dell'immagine, precisamente delle dimensioni <p _dst> se tale parametro e'
  //                     minore di <p _pst>
{                                       
  RCT dst = _src;
  xvt_rect_offset(&dst, x, y);
  xvt_dwin_draw_image(w, _image, &dst, (RCT*)&_src);
}

// Certified 100%
void TImage::draw(WINDOW w, const RCT& dst) const
{
  xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&_src);
}

// Certified 100%
void TImage::draw(WINDOW w, const RCT& dst, const RCT& src) const
{
  xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&src);
}

// Certified 99%
// @doc EXTERNAL

// @mfunc Fa corrispondere la palette della finestra a quella dell'immagine 
void TImage::set_palette(
  WINDOW w) const // @parm Finestra a cui settare la palette
{
  XVT_PALETTE wp = xvt_vobj_get_palet(w);
  if (wp != NULL)
  {
    XVT_PALETTE p = xvt_palet_create(XVT_PALETTE_USER, 0L);
    const int ncolors = xvt_palet_get_ncolors(wp);
    COLOR* color = new COLOR[ncolors];
    xvt_palet_get_colors(wp, color, ncolors);
    xvt_palet_add_colors(p, color, ncolors);
    delete color;
    xvt_palet_add_colors_from_image(p, _image);
    xvt_vobj_set_palet(w, p);
    xvt_palet_destroy(wp);
  }  
}

// Certified 100%
void TImage::set_clut(byte n, COLOR c)
{
  if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)                  
    xvt_image_set_clut(_image, n, c);
}

void TImage::set_pixel(int x, int y, COLOR col)
{
  xvt_image_set_pixel(_image, x, y, col);
}

COLOR TImage::get_pixel(int x, int y) const
{
  return xvt_image_get_pixel(_image, x, y);
}

// Certified 99%
// @doc EXTERNAL

// @mfunc Setta i colori dell'immagine in modo da renderla trasparente
void TImage::convert_to_default_colors()

  // @comm Legge nell'immagine i colori CYAN e DARK_CYAN e li setta a seconda del colore
  //                     della finestra per fare in modo di rendere trasparenti tali colori.
{
  if (MASK_BACK_COLOR != COLOR_DKCYAN)                  
  { 

		if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)
		{
			for (int index = xvt_image_get_ncolors(_image)-1; index >=0; index--)
			{
				const COLOR c = xvt_image_get_clut(_image, index) & 0x00FFFFFF;
				switch (c)
				{
				case COLOR_DKCYAN & 0x00FFFFFF:
					xvt_image_set_clut(_image, index, MASK_BACK_COLOR); break;
				case COLOR_CYAN & 0x00FFFFFF:
					xvt_image_set_clut(_image, index, MASK_LIGHT_COLOR); break;
				case COLOR_GRAY & 0x00FFFFFF:
					xvt_image_set_clut(_image, index, MASK_DARK_COLOR); break;
				default:
					break;  
				}
			}
		}
		else
		{
			short dx, dy; xvt_image_get_dimensions(_image, &dx, &dy);
			for (short y = 0; y < dy; y++) for (short x = 0; x < dx; x++)
			{     
				const COLOR c = get_pixel(x, y) & 0x00FFFFFF;
				switch (c)
				{
				case COLOR_DKCYAN & 0x00FFFFFF:
					set_pixel(x, y, MASK_BACK_COLOR); break;
				case COLOR_CYAN & 0x00FFFFFF:
					set_pixel(x, y, MASK_LIGHT_COLOR); break;
				case COLOR_GRAY & 0x00FFFFFF:
					set_pixel(x, y, MASK_DARK_COLOR); break;
				default:
					break;  
				}
			}
		}
  } 
}

// @mfunc Setta i colori dell'immagine  in modo da renderla trasparente
void TImage::convert_transparent_color(COLOR transparent)

  // @comm Legge nell'immagine i pixel uguali a quello in alto a sinistra e li setta 
  // uguali allo sfondo delle maschere
{                               
  if (_image == NULL)
		return;  // Null image

	const COLOR trans = get_pixel(0,0) & 0x00FFFFFF;
	if (trans == (transparent & 0x00FFFFFF))
    return;  // Nothing to do

	if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)
	{                  
		for (int index = xvt_image_get_ncolors(_image)-1; index >=0; index--)
			if (trans == (xvt_image_get_clut(_image, index) & 0x00FFFFFF))
			{
				xvt_image_set_clut(_image, index, transparent);
				// break; don't break: replace all colors equal to upper left in the palette
			}
	} 
	else
	{
		short dx, dy; xvt_image_get_dimensions(_image, &dx, &dy);
		for (short y = 0; y < dy; y++) for (short x = 0; x < dx; x++)
		{     
			const COLOR c = get_pixel(x, y);
			if ((c & 0x00FFFFFF) == trans)
				set_pixel(x, y, transparent);
		}
	}
}