/** \file
 * \brief cdiuputil. CD and IUP utilities for the IupControls
 *
 * See Copyright Notice in "iup.h"
 */


#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>

#include <cd.h>

#include "iup.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_drv.h"
#include "iup_image.h"

#include "iup_cdutil.h"


long cdIupConvertColor(const char *color)
{
  unsigned char r, g, b;
  if (iupStrToRGB(color, &r, &g, &b))
    return cdEncodeColor(r, g, b);
  else
    return 0;
}

void cdIupCalcShadows(long bgcolor, long *light_shadow, long *mid_shadow, long *dark_shadow)
{
  int r, bg_r = cdRed(bgcolor);
  int g, bg_g = cdGreen(bgcolor);
  int b, bg_b = cdBlue(bgcolor);

  /* light_shadow */

  int max = bg_r;
  if (bg_g > max) max = bg_g;
  if (bg_b > max) max = bg_b;

  if (255-max < 64)
  {
    r = 255;
    g = 255;
    b = 255;
  }
  else
  {
    /* preserve some color information */
    if (bg_r == max) r = 255;
    else             r = bg_r + (255-max);
    if (bg_g == max) g = 255;
    else             g = bg_g + (255-max);
    if (bg_b == max) b = 255;
    else             b = bg_b + (255-max);
  }

  if (light_shadow) *light_shadow = cdEncodeColor((unsigned char)r, (unsigned char)g, (unsigned char)b); 

  /* dark_shadow */
  r = bg_r - 128;
  g = bg_g - 128;
  b = bg_b - 128;
  if (r < 0) r = 0;
  if (g < 0) g = 0;
  if (b < 0) b = 0;

  if (dark_shadow) *dark_shadow = cdEncodeColor((unsigned char)r, (unsigned char)g, (unsigned char)b); 

  /*   mid_shadow = (dark_shadow+bgcolor)/2    */
  if (mid_shadow) *mid_shadow = cdEncodeColor((unsigned char)((bg_r+r)/2), (unsigned char)((bg_g+g)/2), (unsigned char)((bg_b+b)/2));
}

void cdIupDrawSunkenRect(cdCanvas *canvas, int x1, int y1, int x2, int y2, long light_shadow, long mid_shadow, long dark_shadow)
{
  cdCanvasForeground(canvas, mid_shadow);
  cdCanvasLine(canvas, x1, y1+1,   x1, y2);
  cdCanvasLine(canvas, x1,  y2, x2-1, y2);

  cdCanvasForeground(canvas, dark_shadow);
  cdCanvasLine(canvas, x1+1, y1+2, x1+1, y2-1);
  cdCanvasLine(canvas, x1+1, y2-1, x2-2, y2-1);

  cdCanvasForeground(canvas, light_shadow);
  cdCanvasLine(canvas, x1, y1, x2, y1);
  cdCanvasLine(canvas, x2, y1, x2, y2);
}

void cdIupDrawRaiseRect(cdCanvas *canvas, int x1, int y1, int x2, int y2, long light_shadow, long mid_shadow, long dark_shadow)
{
  cdCanvasForeground(canvas, light_shadow);
  cdCanvasLine(canvas, x1, y1+1,   x1, y2);
  cdCanvasLine(canvas, x1,  y2, x2-1, y2);

  cdCanvasForeground(canvas, dark_shadow);
  cdCanvasLine(canvas, x1, y1, x2, y1);
  cdCanvasLine(canvas, x2, y1, x2, y2);

  cdCanvasForeground(canvas, mid_shadow);
  cdCanvasLine(canvas, x1+1, y1+1, x2-1, y1+1);
  cdCanvasLine(canvas, x2-1, y1+2, x2-1, y2-1);
}

void cdIupDrawVertSunkenMark(cdCanvas *canvas, int x, int y1, int y2, long light_shadow, long dark_shadow)
{
  cdCanvasForeground(canvas, dark_shadow);
  cdCanvasLine(canvas, x-1, y1, x-1, y2);
  cdCanvasForeground(canvas, light_shadow);
  cdCanvasLine(canvas,   x, y1,   x, y2);
}

void cdIupDrawHorizSunkenMark(cdCanvas *canvas, int x1, int x2, int y, long light_shadow, long dark_shadow)
{
  cdCanvasForeground(canvas, dark_shadow);
  cdCanvasLine(canvas, x1, y+1, x2, y+1);
  cdCanvasForeground(canvas, light_shadow);
  cdCanvasLine(canvas, x1, y, x2, y);
}


/************************************************************************/


static void iUtilInitPalette(Ihandle* image, long* palette, long bgcolor, int make_inactive)
{
  int i;
  unsigned char r, g, b, bg_r, bg_g, bg_b;

  cdDecodeColor(bgcolor, &bg_r, &bg_g, &bg_b);

  for(i = 0; i < 256; i++)
  {
    char* color = IupGetAttributeId(image, "", i);
    r = bg_r; g = bg_g; b = bg_b;
    iupStrToRGB(color, &r, &g, &b);  /* no need to test for BGCOLOR, if this failed it will not set the parameters */

    if (make_inactive)
      iupImageColorMakeInactive(&r, &g, &b, bg_r, bg_g, bg_b);

    palette[i] = cdEncodeColor(r, g, b);
  }
}

static unsigned char* iUtilBuildImageBuffer(Ihandle *image, int width, int height, int depth, int make_inactive, long bgcolor)
{
  int size, plane_size, i, j;
  unsigned char bg_r, bg_g, bg_b;
  unsigned char* image_buffer;
  /* images from stock or resources are not supported */
  unsigned char* data = (unsigned char*)IupGetAttribute(image, "WID");
  if (!data)
    return NULL;

  plane_size = width*height;
  size = plane_size*depth;

  image_buffer = malloc(size);
  if (!image_buffer)
    return NULL;

  cdDecodeColor(bgcolor, &bg_r, &bg_g, &bg_b);

  /* IUP image is top-down, CD image is bottom up */
  if (depth==1)
  {
    /* inactive will be set at the palette */
    for (i=0; i<height; i++)
      memcpy(image_buffer + i*width, data + (height-1 - i)*width, width);
  }
  else if (depth==3)
  {
    int pos;
    unsigned char r, g, b;
    unsigned char *dst_r = image_buffer + 0*plane_size;
    unsigned char *dst_g = image_buffer + 1*plane_size;
    unsigned char *dst_b = image_buffer + 2*plane_size;

    for (i=0; i<height; i++)
    {
      int line_offset = i*width;
      unsigned char *line_r = dst_r + line_offset;
      unsigned char *line_g = dst_g + line_offset;
      unsigned char *line_b = dst_b + line_offset;
      unsigned char *src_rgb = data + (height-1 - i)*(3*width);

      for (j=0; j<width; j++)
      {
        pos = j*3;
        r = src_rgb[pos+0];
        g = src_rgb[pos+1];
        b = src_rgb[pos+2];

        if (make_inactive)
          iupImageColorMakeInactive(&r, &g, &b, bg_r, bg_g, bg_b);

        line_r[j] = r;
        line_g[j] = g;
        line_b[j] = b;
      }
    }
  }
  else /* depth==4 */
  {
    int pos;
    unsigned char r, g, b, a;
    unsigned char *dst_r = image_buffer + 0*plane_size;
    unsigned char *dst_g = image_buffer + 1*plane_size;
    unsigned char *dst_b = image_buffer + 2*plane_size;
    unsigned char *dst_a = image_buffer + 3*plane_size;

    for (i=0; i<height; i++)
    {
      int line_offset = i*width;
      unsigned char *line_r = dst_r + line_offset;
      unsigned char *line_g = dst_g + line_offset;
      unsigned char *line_b = dst_b + line_offset;
      unsigned char *line_a = dst_a + line_offset;
      unsigned char *src_rgba = data + (height-1 - i)*(4*width);

      for (j=0; j<width; j++)
      {
        pos = j*4;
        r = src_rgba[pos+0];
        g = src_rgba[pos+1];
        b = src_rgba[pos+2];
        a = src_rgba[pos+3];

        if (make_inactive)
          iupImageColorMakeInactive(&r, &g, &b, bg_r, bg_g, bg_b);

        line_r[j] = r;
        line_g[j] = g;
        line_b[j] = b;
        line_a[j] = a;
      }
    }
  }

  return image_buffer;
}

void cdIupDrawImage(cdCanvas *canvas, Ihandle *image, int x, int y, int w, int h, int make_inactive, long bgcolor)
{
  int plane_size, depth;
  int width = IupGetInt(image, "WIDTH");
  int height = IupGetInt(image, "HEIGHT");
  int bpp = IupGetInt(image, "BPP");
  unsigned char* image_buffer;

  depth = 1;
  if (bpp==24)
    depth = 3;
  else if (bpp==32)
    depth = 4;

  if (w == 0) w = width;
  if (h == 0) h = height;

  /* The buffer will be destroyed in IupImage unmap */
  if (depth!=1 && make_inactive)
    image_buffer = (unsigned char*)iupAttribGet(image, "_IUPIMAGE_BUFFER_INACTIVE");
  else
    image_buffer = (unsigned char*)iupAttribGet(image, "_IUPIMAGE_BUFFER");

  if (!image_buffer)
  {
    image_buffer = iUtilBuildImageBuffer(image, width, height, depth, make_inactive, bgcolor);

    if (depth!=1 && make_inactive)
      iupAttribSet(image, "_IUPIMAGE_BUFFER_INACTIVE", (char*)image_buffer);
    else
      iupAttribSet(image, "_IUPIMAGE_BUFFER", (char*)image_buffer);
  }

  if (!image_buffer)
    return;

  plane_size = width*height;

  if (depth==1)
  {
    long palette[256];
    iUtilInitPalette(image, palette, bgcolor, make_inactive);

    cdCanvasPutImageRectMap(canvas, width, height, 
                            image_buffer, palette, 
                            x, y, w, h, 0, 0, 0, 0);
  }
  else if (depth==3)
  {
    cdCanvasPutImageRectRGB(canvas, width, height,
                            image_buffer + 0*plane_size,
                            image_buffer + 1*plane_size,
                            image_buffer + 2*plane_size,
                            x, y, w, h, 0, 0, 0, 0);
  }
  else /* depth==4 */
  {
    cdCanvasPutImageRectRGBA(canvas, width, height,
                             image_buffer + 0*plane_size,
                             image_buffer + 1*plane_size,
                             image_buffer + 2*plane_size,
                             image_buffer + 3*plane_size,
                             x, y, w, h, 0, 0, 0, 0);
  }
}


/************************************************************************/


void IupCdSetFont(Ihandle* ih, cdCanvas *canvas, const char* font)
{
  char* lastfont = iupAttribGetStr(ih, "_IUPCD_LASTFONT");
  if (!lastfont || !iupStrEqual(lastfont, font))
  {
    iupAttribSetStr(ih, "_IUPCD_LASTFONT", font);
    cdCanvasNativeFont(canvas, font);
  }
}

void cdIupDrawFocusRect(cdCanvas *canvas, int x1, int y1, int x2, int y2)
{
  int style = cdCanvasLineStyle(canvas, CD_QUERY);
  long foreground = cdCanvasForeground(canvas, CD_QUERY);
  cdCanvasLineStyle(canvas, CD_DOTTED);
  cdCanvasForeground(canvas, CD_BLACK);
  cdCanvasRect(canvas, x1, x2, y1, y2);
  cdCanvasLineStyle(canvas, style);
  cdCanvasForeground(canvas, foreground);
}

