#include <stdlib.h>
#include <iostream>
#include <ClanLib/core.h>
#include <ClanLib/display.h>
#include <ClanLib/gl.h>

#define WIDTH 2

struct Color
{
  inline Color(unsigned char r, unsigned char g, unsigned char b)
    : red(r), green(g), blue(b) {}

  unsigned char red;
  unsigned char green;
  unsigned char blue;

  inline bool operator!=(const Color& c) const
  {
    return 
      red   != c.red ||
      green != c.green ||
      blue  != c.blue;
  }
};

Color black(0, 0, 0);

inline float dist(const Color& a, const Color& b)
{
  float d = (abs(int(a.red)   - int(b.red)) +
             abs(int(a.green) - int(b.green)) + 
             abs(int(a.blue)  - int(b.blue))) / 3.0f / 255.0f;
  return d;
}

inline Color get_pixel(CL_PixelBuffer buffer, int x, int y)
{
  unsigned char* buf = static_cast<unsigned char*>(buffer.get_data());
  return Color(buf[(buffer.get_pitch() * y) + 3*x + 0],
                  buf[(buffer.get_pitch() * y) + 3*x + 1],
                  buf[(buffer.get_pitch() * y) + 3*x + 2]);
}

inline void put_pixel(CL_PixelBuffer buffer, int x, int y, const Color& color)
{
  unsigned char* buf = static_cast<unsigned char*>(buffer.get_data());
  buf[(buffer.get_pitch() * y) + 3*x + 0] = color.red;
  buf[(buffer.get_pitch() * y) + 3*x + 1] = color.green;
  buf[(buffer.get_pitch() * y) + 3*x + 2] = color.blue;
}


void filter_pixel(CL_PixelBuffer data, CL_PixelBuffer out,
                  int pitch, int height, int x_pos, int y_pos)
{
  Color color(0,0,0);

  for(int y = y_pos - WIDTH; y < y_pos + WIDTH; ++y)
    {
      for(int x = x_pos - WIDTH; x < x_pos + WIDTH; ++x)
        {
          const Color& c = get_pixel(data, x, y);
          if (c != black)
            {
              color = c;
              goto done;
            }
        }
    }

 done:
  put_pixel(out, x_pos, y_pos, color);
}

inline void filter(CL_PixelBuffer buffer, CL_PixelBuffer out)
{
  buffer.lock();
  out.lock();
  
  int pitch  = buffer.get_pitch();
  int height = buffer.get_height();
  int width  = buffer.get_width();

  for(int y = WIDTH; y < height-WIDTH; ++y)
    {
      std::cout << "." << std::flush;

      for(int x = WIDTH; x < width-WIDTH; ++x)
        {
          filter_pixel(buffer, out, pitch, height, x, y);
        }  
    }

  buffer.unlock();
  out.unlock();
}


int main(int argc, char** argv)
{
  CL_SetupCore setup_core;
  CL_SetupDisplay setup_display;
  CL_SetupGL setup_gl;
  
  CL_PixelBuffer buffer1;
  CL_PixelBuffer buffer2;
  
  buffer1 = CL_ProviderFactory::load(argv[1]);
  buffer2 = CL_ProviderFactory::load(argv[2]);

  assert(buffer1.get_format().get_depth() == 24);
  assert(buffer2.get_format().get_depth() == 24);

  int width  = buffer1.get_width();
  int height = buffer1.get_height();

  CL_PixelBuffer result = CL_PixelBuffer(width, height,  width * 3, CL_PixelFormat::rgb888);
  CL_PixelBuffer result2 = CL_PixelBuffer(width, height,  width * 3, CL_PixelFormat::rgb888);

  buffer1.lock();
  buffer2.lock();

  for(int y = 0; y < height; ++y)
    for(int x = 0; x < width; ++x)
      {
        Color p1 = get_pixel(buffer1, x, y);
        Color p2 = get_pixel(buffer2, x, y);

        if (dist(p1, p2) < 0.1f)
          {
            put_pixel(result, x, y, Color(0, 0, 0));
          }
        else
          {
            put_pixel(result, x, y, p2);
          }
      }
    
  buffer1.unlock();
  buffer2.unlock();

  CL_ProviderFactory::save(result, "out1.png");
  std::cout << "Running filter..." << std::endl;

  filter(result, result2);
  filter(result2, result);
  filter(result, result2);
  filter(result2, result);
  filter(result, result2);

  CL_ProviderFactory::save(result2, "filtered.png");
};

/* EOF */

