/**
 * Graphics::PNG is PNG library
 *
 * color.c include three classes:
 *  Graphics::PNG::Color, Graphics::PNG::Color16, Graphics::PNG::Color8.
 * These classes are wrapper objects for libpng's color struct.
 *
 * @author DATE Ken (as Itacchi) / ge6537@i.bekkoame.ne.jp
 * @code-name Aoi
 * @see http://www.isc.meiji.ac.jp/~ee77038/ruby/
 */

#include "libpng.h"

VALUE cPngColor;
VALUE cPngColor16;
VALUE cPngColor8;


/*
 * --------------------
 * Graphics::PNG::Color
 * --------------------
 * is a wrapper object of the libpng's png_color struct.
 * This will be replaced to one Color class like as java.awt.Color.
 */
/*
=begin
= (({Graphics::PNG::Color}))
A wrapper object of the libpng's png_color struct.


=== class method

--- Graphics::PNG::Color.new
    Create PNG Color object.


=== instance method

--- Graphics::PNG::Color#blue
    Get blue color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#blue=(val)
    Set blue color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#green
    Get green color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#green=(val)
    Set green color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#red
    Get red color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#red=(val)
    Set red color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).
=end
*/


/*
 * ----------------------
 * utility function for
 *   Graphics::PNG::Color
 * ----------------------
 */
void
libpng_color_check_type(obj)
  VALUE obj;
{
  if (rb_class_of(obj) != cPngColor){
    rb_raise(rb_eTypeError,
             "wrong argument type %s (expected Graphics::PNG::Color)",
             rb_class2name(CLASS_OF(obj)));
  }
}




/*
 * ----------------------
 * class method of
 *   Graphics::PNG::Color
 * ----------------------
 */
static VALUE
libpng_color_new(klass)
  VALUE klass;
{
  VALUE new_obj;
  png_colorp color;

  new_obj = Data_Make_Struct(klass, png_color, NULL, free, color);

  color->red   = 0;
  color->green = 0;
  color->blue  = 0;

  return new_obj;
}



VALUE
libpng_color_new2(klass, source_color)
  VALUE klass;
  png_colorp source_color;
{
  VALUE new_obj;
  png_colorp color;

  new_obj = Data_Make_Struct(klass, png_color, NULL, free, color);

  color->red   = source_color->red;
  color->green = source_color->green;
  color->blue  = source_color->blue;

  return new_obj;
}




/*
 * ----------------------
 * instance method of
 *   Graphics::PNG::Color
 * ----------------------
 */
static VALUE
libpng_color_initialize(obj)
  VALUE obj;
{
  return Qnil;
}



static VALUE
libpng_color_get_blue(obj)
  VALUE obj;
{
  png_colorp color;

  Data_Get_Struct(obj, png_color, color);

  return CHR2FIX(color->blue);
}



static VALUE
libpng_color_get_green(obj)
  VALUE obj;
{
  png_colorp color;

  Data_Get_Struct(obj, png_color, color);

  return CHR2FIX(color->green);
}



static VALUE
libpng_color_get_red(obj)
  VALUE obj;
{
  png_colorp color;

  Data_Get_Struct(obj, png_color, color);

  return CHR2FIX(color->red);
}



static VALUE
libpng_color_inspect(obj)
  VALUE obj;
{
  png_colorp color;
  char buf[256];
  VALUE str;

  Data_Get_Struct(obj, png_color, color);

  str = rb_str_new2("#<PNG::Color: ");

  sprintf(buf, "@red=%d, ", color->red);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@green=%d, ", color->green);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@blue=%d", color->blue);
  rb_str_cat(str, buf, strlen(buf));

  rb_str_cat(str, ">", 1);

  return str;
}



static VALUE
libpng_color_set_blue(obj, val)
  VALUE obj, val;
{
  png_colorp color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color, color);

  color->blue = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_set_green(obj, val)
  VALUE obj, val;
{
  png_colorp color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color, color);

  color->green = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_set_red(obj, val)
  VALUE obj, val;
{
  png_colorp color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color, color);

  color->red = NUM2CHR(val);

  return Qnil;
}




/*
 * ----------------------
 * Graphics::PNG::Color16
 * ----------------------
 * is a wrapper object of the libpng's png_color_16 struct.
 * This will be replaced to one Color class like as java.awt.Color.
 */
/*
=begin
= (({Graphics::PNG::Color16}))
A wrapper object of the libpng's png_color_16 struct.


=== class method

--- Graphics::PNG::Color16.new
    Create PNG 16bits Color object.


=== instance method

--- Graphics::PNG::Color16#blue
    Get blue color value (in RGB color images).
    Range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#blue=(val)
    Set blue color value (in RGB color images).
    Valid range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#gray
    Get grayscale value (for use in grayscale images).
    Range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#gray=(val)
    Set grayscale value (for use in grayscale images).
    Valid range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#green
    Get green color value (in RGB color images).
    Range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#index=(val)
    Set color index number (used for palette images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color16#index
    Get color index number (used for palette images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color16#green=(val)
    Set green color value (in RGB color images).
    Valid range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#red
    Get red color value (in RGB color images).
    Range of value is [0, 65535] (16bits).

--- Graphics::PNG::Color16#red=(val)
    Set red color value (in RGB color images).
    Valid range of value is [0, 65535] (16bits).
=end
*/

/*
 * ------------------------
 * utility function for
 *   Graphics::PNG::Color16
 * ------------------------
 */
void
libpng_color_16_check_type(obj)
  VALUE obj;
{
  if (rb_class_of(obj) != cPngColor16){
    rb_raise(rb_eTypeError,
             "wrong argument type %s (expected Graphics::PNG::Color16)",
             rb_class2name(CLASS_OF(obj)));
  }
}




/*
 * ------------------------
 * class method of
 *   Graphics::PNG::Color16
 * ------------------------
 */
static VALUE
libpng_color_16_new(klass)
  VALUE klass;
{
  VALUE new_obj;
  png_color_16p color;

  new_obj = Data_Make_Struct(klass, png_color_16, NULL, free, color);

  color->index = 0;
  color->red   = 0;
  color->green = 0;
  color->blue  = 0;
  color->gray  = 0;

  return new_obj;
}



VALUE
libpng_color_16_new2(klass, source_color)
  VALUE klass;
  png_color_16p source_color;
{
  VALUE new_obj;
  png_color_16p color;

  new_obj = Data_Make_Struct(klass, png_color_16, NULL, free, color);

  color->index = source_color->index;
  color->red   = source_color->red;
  color->green = source_color->green;
  color->blue  = source_color->blue;
  color->gray  = source_color->gray;

  return new_obj;
}




/*
 * ------------------------
 * instance method of
 *   Graphics::PNG::Color16
 * ------------------------
 */
static VALUE
libpng_color16_initialize(obj)
  VALUE obj;
{
  return Qnil;
}



static VALUE
libpng_color_16_get_blue(obj)
  VALUE obj;
{
  png_color_16p color;

  Data_Get_Struct(obj, png_color_16, color);

  return INT2FIX(color->blue);
}



static VALUE
libpng_color_16_get_gray(obj)
  VALUE obj;
{
  png_color_16p color;

  Data_Get_Struct(obj, png_color_16, color);

  return INT2FIX(color->gray);
}



static VALUE
libpng_color_16_get_green(obj)
  VALUE obj;
{
  png_color_16p color;

  Data_Get_Struct(obj, png_color_16, color);

  return INT2FIX(color->green);
}



static VALUE
libpng_color_16_get_index(obj)
  VALUE obj;
{
  png_color_16p color;

  Data_Get_Struct(obj, png_color_16, color);

  return CHR2FIX(color->index);
}



static VALUE
libpng_color_16_get_red(obj)
  VALUE obj;
{
  png_color_16p color;

  Data_Get_Struct(obj, png_color_16, color);

  return INT2FIX(color->red);
}



static VALUE
libpng_color_16_inspect(obj)
  VALUE obj;
{
  png_color_16p color;
  char buf[256];
  VALUE str;

  Data_Get_Struct(obj, png_color_16, color);

  str = rb_str_new2("#<PNG::Color16: ");

  sprintf(buf, "@index=%d, ", color->index);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@red=%d, ", color->red);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@green=%d, ", color->green);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@blue=%d, ", color->blue);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@gray=%d", color->gray);
  rb_str_cat(str, buf, strlen(buf));

  rb_str_cat(str, ">", 1);

  return str;
}



static VALUE
libpng_color_16_set_blue(obj, val)
  VALUE obj, val;
{
  png_color_16p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_16, color);

  color->blue = FIX2INT(val);

  return Qnil;
}



static VALUE
libpng_color_16_set_gray(obj, val)
  VALUE obj, val;
{
  png_color_16p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_16, color);

  color->gray = FIX2INT(val);

  return Qnil;
}



static VALUE
libpng_color_16_set_green(obj, val)
  VALUE obj, val;
{
  png_color_16p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_16, color);

  color->green = FIX2INT(val);

  return Qnil;
}



static VALUE
libpng_color_16_set_index(obj, val)
  VALUE obj, val;
{
  png_color_16p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_16, color);

  color->index = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_16_set_red(obj, val)
  VALUE obj, val;
{
  png_color_16p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_16, color);

  color->red = FIX2INT(val);

  return Qnil;
}




/*
 * ---------------------
 * Graphics::PNG::Color8
 * ---------------------
 * is also a wrapper object of the libpng's png_color_8 struct.
 * This will be replaced to one Color class like as java.awt.Color.
 */
/*
=begin
= (({Graphics::PNG::Color8}))
A wrapper object of the libpng's png_color_8 struct.


=== class method

--- Graphics::PNG::Color8.new
    Create PNG 8bits Color object.


=== instance method

--- Graphics::PNG::Color8#alpha
    Get blue color value (for alpha channel images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#alpha=(val)
    Set blue color value (for alpha channel images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#blue
    Get blue color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#blue=(val)
    Set blue color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#gray
    Get grayscale value (for use in grayscale images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#gray=(val)
    Set grayscale value (for use in grayscale images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#green
    Get green color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#green=(val)
    Set green color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).

--- Graphics::PNG::Color8#red
    Get red color value (in RGB color images).
    Range of value is [0, 255] (8bits).

--- Graphics::PNG::Color#red=(val)
    Set red color value (in RGB color images).
    Valid range of value is [0, 255] (8bits).
=end
*/


/*
 * -----------------------
 * utility function for
 *   Graphics::PNG::Color8
 * -----------------------
 */
void
libpng_color_8_check_type(obj)
  VALUE obj;
{
  if (rb_class_of(obj) != cPngColor8){
    rb_raise(rb_eTypeError,
             "wrong argument type %s (expected Graphics::PNG::Color8)",
             rb_class2name(CLASS_OF(obj)));
  }
}




/*
 * -----------------------
 * class method of
 *   Graphics::PNG::Color8
 * -----------------------
 */
static VALUE
libpng_color_8_new(klass)
  VALUE klass;
{
  VALUE new_obj;
  png_color_8p color;

  new_obj = Data_Make_Struct(klass, png_color_8, NULL, free, color);

  color->red   = 0;
  color->green = 0;
  color->blue  = 0;
  color->gray  = 0;
  color->alpha = 0;

  return new_obj;
}



VALUE
libpng_color_8_new2(klass, source_color)
  VALUE klass;
  png_color_8p source_color;
{
  VALUE new_obj;
  png_color_8p color;

  new_obj = Data_Make_Struct(klass, png_color_8, NULL, free, color);

  color->red   = source_color->red;
  color->green = source_color->green;
  color->blue  = source_color->blue;
  color->gray  = source_color->gray;
  color->alpha = source_color->alpha;

  return new_obj;
}




/*
 * -----------------------
 * instance method of
 *   Graphics::PNG::Color8
 * -----------------------
 */
static VALUE
libpng_color8_initialize(obj)
  VALUE obj;
{
  return Qnil;
}



static VALUE
libpng_color_8_get_alpha(obj)
  VALUE obj;
{
  png_color_8p color;

  Data_Get_Struct(obj, png_color_8, color);

  return CHR2FIX(color->alpha);
}



static VALUE
libpng_color_8_get_blue(obj)
  VALUE obj;
{
  png_color_8p color;

  Data_Get_Struct(obj, png_color_8, color);

  return CHR2FIX(color->blue);
}



static VALUE
libpng_color_8_get_gray(obj)
  VALUE obj;
{
  png_color_8p color;

  Data_Get_Struct(obj, png_color_8, color);

  return CHR2FIX(color->gray);
}



static VALUE
libpng_color_8_get_green(obj)
  VALUE obj;
{
  png_color_8p color;

  Data_Get_Struct(obj, png_color_8, color);

  return CHR2FIX(color->green);
}



static VALUE
libpng_color_8_get_red(obj)
  VALUE obj;
{
  png_color_8p color;

  Data_Get_Struct(obj, png_color_8, color);

  return CHR2FIX(color->red);
}



static VALUE
libpng_color_8_inspect(obj)
  VALUE obj;
{
  png_color_8p color;
  char buf[256];
  VALUE str;

  Data_Get_Struct(obj, png_color_8, color);

  str = rb_str_new2("#<PNG::Color16: ");

  sprintf(buf, "@red=%d, ", color->red);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@green=%d, ", color->green);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@blue=%d, ", color->blue);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@gray=%d, ", color->gray);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@alpha=%d", color->alpha);
  rb_str_cat(str, buf, strlen(buf));

  rb_str_cat(str, ">", 1);

  return str;
}



static VALUE
libpng_color_8_set_alpha(obj, val)
  VALUE obj, val;
{
  png_color_8p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_8, color);

  color->alpha = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_8_set_blue(obj, val)
  VALUE obj, val;
{
  png_color_8p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_8, color);

  color->blue = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_8_set_gray(obj, val)
  VALUE obj, val;
{
  png_color_8p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_8, color);

  color->gray = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_8_set_green(obj, val)
  VALUE obj, val;
{
  png_color_8p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_8, color);

  color->green = NUM2CHR(val);

  return Qnil;
}



static VALUE
libpng_color_8_set_red(obj, val)
  VALUE obj, val;
{
  png_color_8p color;

  FIXNUM_P(val);

  Data_Get_Struct(obj, png_color_8, color);

  color->red = NUM2CHR(val);

  return Qnil;
}




void
Init_color()
{
  cPngColor   = rb_define_class_under(mPng, "Color",   rb_cObject);
  cPngColor16 = rb_define_class_under(mPng, "Color16", rb_cObject);
  cPngColor8  = rb_define_class_under(mPng, "Color8",  rb_cObject);


  /*
   * ------------------------------------
   * Graphics::PNG::Color
   * ------------------------------------
   */

  rb_define_singleton_method(cPngColor, "new", libpng_color_new, 0);

  rb_define_method(cPngColor, "initialize", libpng_color_initialize, -1);

  rb_define_method(cPngColor, "blue",  libpng_color_get_blue,  0);
  rb_define_method(cPngColor, "green", libpng_color_get_green, 0);
  rb_define_method(cPngColor, "red",   libpng_color_get_red,   0);

  rb_define_method(cPngColor, "inspect", libpng_color_inspect, 0);

  rb_define_method(cPngColor, "blue=",  libpng_color_set_blue,  1);
  rb_define_method(cPngColor, "green=", libpng_color_set_green, 1);
  rb_define_method(cPngColor, "red=",   libpng_color_set_red,   1);


  /*
   * ------------------------------------
   * Graphics::PNG::Color16
   * ------------------------------------
   */

  rb_define_singleton_method(cPngColor16, "new", libpng_color_16_new, 0);

  rb_define_method(cPngColor16, "initialize", libpng_color16_initialize, -1);

  rb_define_method(cPngColor16, "blue",  libpng_color_16_get_blue,  0);
  rb_define_method(cPngColor16, "gray",  libpng_color_16_get_gray,  0);
  rb_define_method(cPngColor16, "green", libpng_color_16_get_green, 0);
  rb_define_method(cPngColor16, "index", libpng_color_16_get_index, 0);
  rb_define_method(cPngColor16, "red",   libpng_color_16_get_red,   0);

  rb_define_method(cPngColor16, "inspect", libpng_color_16_inspect, 0);

  rb_define_method(cPngColor16, "blue=",  libpng_color_16_set_blue,  1);
  rb_define_method(cPngColor16, "gray=",  libpng_color_16_set_gray,  1);
  rb_define_method(cPngColor16, "green=", libpng_color_16_set_green, 1);
  rb_define_method(cPngColor16, "index=", libpng_color_16_set_index, 1);
  rb_define_method(cPngColor16, "red=",   libpng_color_16_set_red,   1);


  /*
   * ------------------------------------
   * Graphics::PNG::Color8
   * ------------------------------------
   */

  rb_define_singleton_method(cPngColor8, "new", libpng_color_8_new, 0);

  rb_define_method(cPngColor8, "initialize", libpng_color8_initialize, -1);

  rb_define_method(cPngColor8, "alpha", libpng_color_8_get_alpha, 0);
  rb_define_method(cPngColor8, "blue",  libpng_color_8_get_blue,  0);
  rb_define_method(cPngColor8, "gray",  libpng_color_8_get_gray,  0);
  rb_define_method(cPngColor8, "green", libpng_color_8_get_green, 0);
  rb_define_method(cPngColor8, "red",   libpng_color_8_get_red,   0);

  rb_define_method(cPngColor8, "inspect", libpng_color_8_inspect, 0);

  rb_define_method(cPngColor8, "alpha=", libpng_color_8_set_alpha, 1);
  rb_define_method(cPngColor8, "blue=",  libpng_color_8_set_blue,  1);
  rb_define_method(cPngColor8, "gray=",  libpng_color_8_set_gray,  1);
  rb_define_method(cPngColor8, "green=", libpng_color_8_set_green, 1);
  rb_define_method(cPngColor8, "red=",   libpng_color_8_set_red,   1);
}
