#include "imagecon.h"

static void
_dither_createDither(imagecon_image_t* ic);

amiga_color_t
dither_getPalettedColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last)
{
  return color_findClosestPalettePixel(ic, color);
}


amiga_color_t
dither_getHamColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last)
{
  ham_control_t ham = color_findClosestHamPixel(ic, color, last);
  return ham.pixel;
}

float
_gamma(float x)
{
     return x * 0.55;
     //  return x;
}

static void
_propagateError(imagecon_image_t* ic, float factor, int x, int y, dither_color_t error)
{
  if (x >= 0 && y >= 0 && x < ic->width && y < ic->height) {
    dither_color_t color = color_getDitheredPixel(ic, x, y);
    color.r += _gamma(error.r * factor);
    color.g += _gamma(error.g * factor);
    color.b += _gamma(error.b * factor);    
    color_setDitheredPixel(ic, x, y, color);
  }
}

void
dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t*, amiga_color_t color, amiga_color_t last))
{
  _dither_createDither(ic);
  
  for (int y = 0; y < ic->height; y++) {
    amiga_color_t last = {-1, -1, -1, -1};
    for (int x = 0; x < ic->width; x++) {

      dither_color_t old = color_getDitheredPixel(ic, x, y);
      amiga_color_t new = selector(ic, color_ditheredToAmiga(old), last);

      dither_color_t error;
      float gamma = 1.0;
      error.r = old.r - (new.r*gamma);
      error.g = old.g - (new.g*gamma);
      error.b = old.b - (new.b*gamma);

      color_setDitheredPixel(ic, x, y, color_amigaToDithered(new));
      last = new;

      _propagateError(ic, 7.0/16.0, x+1, y,   error);
      _propagateError(ic, 3.0/16.0, x-1, y+1, error);
      _propagateError(ic, 5.0/16.0, x  , y+1, error);
      _propagateError(ic, 1.0/16.0, x+1, y+1, error);     
    }

  }

}


void
dither_transferToPaletted(imagecon_image_t* ic)
{
  for (int y = 0; y < ic->height; y++) {
    for (int x = 0; x < ic->width; x++) {
      color_setPalettedPixel(ic, x, y, color_ditheredToAmiga(color_getDitheredPixel(ic, x, y)));
    }
  }
}


ham_control_t* 
dither_createHams(imagecon_image_t* ic)
{
  ham_control_t* hams = malloc(sizeof(ham_control_t)*ic->width*ic->height);

  for (int y = 0; y < ic->height; y++) {
    amiga_color_t lastPixel = { -1, -1, -1, -1};
    for (int x = 0; x < ic->width; x++) {
      amiga_color_t orig = color_ditheredToAmiga(color_getDitheredPixel(ic, x, y));
      ham_control_t ham = color_findClosestHamPixel(ic, orig, lastPixel);
      lastPixel = ham.pixel;
      hams[(y*ic->width)+x] = ham;
    }
  }

  return hams;
}


static void
_dither_createDither(imagecon_image_t* ic)
{
  ic->dithered = malloc(sizeof(dither_color_t)*ic->width*ic->height);
  
  for (int y = 0; y < ic->height; y++) {
    for (int x = 0; x < ic->width; x++) {
      color_setDitheredPixel(ic, x, y, color_amigaToDithered(color_getOriginalPixel(ic, x, y)));
    }
  }
}
