/**
 * Graphics::PNG is PNG library
 *
 * libpng.c defines two modules and one exception class and many constants,
 * and initializing library.
 *
 * @author DATE Ken (as Itacchi) / ge6537@i.bekkoame.ne.jp
 * @code-name Aoi
 * @see http://www.isc.meiji.ac.jp/~ee77038/ruby/
 * $Id: libpng.c,v 1.1 2000/09/27 17:08:29 date Exp date $
 */

#include "libpng.h"

VALUE mGraphics;
VALUE mPng;
VALUE ePngError;



/*
 * --------------------
 * utility function for
 *   Graphics::PNG
 * --------------------
 */
/* Check_Type(x, T_TIME) */
void
time_check_type(x)
  VALUE x;
{
  char *etype;

  if (rb_class_of(x) != rb_cTime){
    etype = RSTRING(rb_obj_as_string(x))->ptr;
    rb_raise(rb_eTypeError,
             "wrong argument type %s (expected %s)",
             etype, "Time");
  }
}




/*
 * ---------------
 * class method of
 *   Graphics::PNG
 * ---------------
 */
static VALUE
libpng_get_copyright(klass)
  VALUE klass;
{
  return rb_str_new2(png_get_copyright(NULL));
}



static VALUE
libpng_get_header_ver(klass)
  VALUE klass;
{
  return rb_str_new2(png_get_header_ver(NULL));
}



static VALUE
libpng_get_header_version(klass)
  VALUE klass;
{
  return rb_str_new2(png_get_header_version(NULL));
}



static VALUE
libpng_get_libpng_ver(klass)
  VALUE klass;
{
  return rb_str_new2(png_get_libpng_ver(NULL));
}



VALUE
libpng_is_png_p(klass, file)
  VALUE klass, file;
{
  char buf[PNG_BYTES_TO_CHECK];
  OpenFile *rfptr;

  /* `file' excepts File or String Object */

  if (TYPE(file) == T_STRING){
    NEWOBJ(rfile, struct RFile);
    OBJSETUP(rfile, rb_cFile, T_FILE);
    MakeOpenFile(rfile, rfptr);

    rfptr->mode = rb_io_mode_flags("rb");
    rfptr->f    = fopen(RSTRING(file)->ptr, "rb");
    rfptr->path = strdup(RSTRING(file)->ptr);
    rb_io_check_readable(rfptr);

    /* return false if fail to open file */
    if (rfptr->f == NULL)
      return Qfalse;

    if (fread(buf, 1, PNG_BYTES_TO_CHECK, GetReadFile(rfptr)) != PNG_BYTES_TO_CHECK)
      return Qfalse;

    if (!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK))
      return Qtrue;
  }
  else if (TYPE(file) == T_FILE){
    GetOpenFile(file, rfptr);
    rb_io_check_readable(rfptr);

    if (fread(buf, 1, PNG_BYTES_TO_CHECK, GetReadFile(rfptr)) != PNG_BYTES_TO_CHECK)
      return Qfalse;

    if (!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK))
      return Qtrue;
  }

  return Qfalse;
}




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




void Init_libpng()
{
  mGraphics = rb_define_module("Graphics");
  mPng      = rb_define_module_under(mGraphics, "PNG");
  ePngError = rb_define_class_under(mPng, "PngError", rb_eException);

  /* initializing Graphics::PNG::Color */
  Init_color();

  /* initializing Graphics::PNG::SuggestedPaletteEntry */
  Init_sPLT();

  /* initializing Graphics::PNG::Text */
  Init_text();

  /* initializing Graphics::PNG::UnknownChunk */
  Init_unknown_chunk();

  /* initializing Graphics::PNG::Reader */
  Init_reader();

  /* initializing Graphics::PNG::Writer */
  Init_writer();


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

  /* define constants */
  
  rb_define_const(mPng, "COLOR_MASK_PALETTE", INT2FIX(PNG_COLOR_MASK_PALETTE));
  rb_define_const(mPng, "COLOR_MASK_COLOR",   INT2FIX(PNG_COLOR_MASK_COLOR));
  rb_define_const(mPng, "COLOR_MASK_ALPHA",   INT2FIX(PNG_COLOR_MASK_ALPHA));

  rb_define_const(mPng, "COLOR_TYPE_GRAY",       INT2FIX(PNG_COLOR_TYPE_GRAY));
  rb_define_const(mPng, "COLOR_TYPE_PALETTE",    INT2FIX(PNG_COLOR_TYPE_PALETTE));
  rb_define_const(mPng, "COLOR_TYPE_RGB",        INT2FIX(PNG_COLOR_TYPE_RGB));
  rb_define_const(mPng, "COLOR_TYPE_RGB_ALPHA",  INT2FIX(PNG_COLOR_TYPE_RGB_ALPHA));
  rb_define_const(mPng, "COLOR_TYPE_GRAY_ALPHA", INT2FIX(PNG_COLOR_TYPE_GRAY_ALPHA));

  rb_define_const(mPng, "FILTER_TYPE_BASE", INT2FIX(PNG_FILTER_TYPE_BASE));

  rb_define_const(mPng, "INTERLACE_NONE",  INT2FIX(PNG_INTERLACE_NONE));
  rb_define_const(mPng, "INTERLACE_ADAM7", INT2FIX(PNG_INTERLACE_ADAM7));

  rb_define_const(mPng, "OFFSET_PIXEL",      INT2FIX(PNG_OFFSET_PIXEL));
  rb_define_const(mPng, "OFFSET_MICROMETER", INT2FIX(PNG_OFFSET_MICROMETER));

  rb_define_const(mPng, "EQUATION_LINEAR",     INT2FIX(PNG_EQUATION_LINEAR));
  rb_define_const(mPng, "EQUATION_BASE_E",     INT2FIX(PNG_EQUATION_BASE_E));
  rb_define_const(mPng, "EQUATION_ARBITRARY",  INT2FIX(PNG_EQUATION_ARBITRARY));
  rb_define_const(mPng, "EQUATION_HYPERBOLIC", INT2FIX(PNG_EQUATION_HYPERBOLIC));

  rb_define_const(mPng, "SCALE_UNKNOWN", INT2FIX(PNG_SCALE_UNKNOWN));
  rb_define_const(mPng, "SCALE_METER",   INT2FIX(PNG_SCALE_METER));
  rb_define_const(mPng, "SCALE_RADIAN",  INT2FIX(PNG_SCALE_RADIAN));

  rb_define_const(mPng, "RESOLUTION_UNKNOWN", INT2FIX(PNG_RESOLUTION_UNKNOWN));
  rb_define_const(mPng, "RESOLUTION_METER",   INT2FIX(PNG_RESOLUTION_METER));

  rb_define_const(mPng, "SRGB_INTENT_PERCEPTUAL", INT2FIX(PNG_sRGB_INTENT_PERCEPTUAL));
  rb_define_const(mPng, "SRGB_INTENT_RELATIVE",   INT2FIX(PNG_sRGB_INTENT_RELATIVE));
  rb_define_const(mPng, "SRGB_INTENT_SATURATION", INT2FIX(PNG_sRGB_INTENT_SATURATION));
  rb_define_const(mPng, "SRGB_INTENT_ABSOLUTE",   INT2FIX(PNG_sRGB_INTENT_ABSOLUTE));

  rb_define_const(mPng, "INFO_gAMA", INT2FIX(PNG_INFO_gAMA));
  rb_define_const(mPng, "INFO_sBIT", INT2FIX(PNG_INFO_sBIT));
  rb_define_const(mPng, "INFO_cHRM", INT2FIX(PNG_INFO_cHRM));
  rb_define_const(mPng, "INFO_PLTE", INT2FIX(PNG_INFO_PLTE));
  rb_define_const(mPng, "INFO_tRNS", INT2FIX(PNG_INFO_tRNS));
  rb_define_const(mPng, "INFO_bKGD", INT2FIX(PNG_INFO_bKGD));
  rb_define_const(mPng, "INFO_hIST", INT2FIX(PNG_INFO_hIST));
  rb_define_const(mPng, "INFO_pHYs", INT2FIX(PNG_INFO_pHYs));
  rb_define_const(mPng, "INFO_oFFs", INT2FIX(PNG_INFO_oFFs));
  rb_define_const(mPng, "INFO_tIME", INT2FIX(PNG_INFO_tIME));
  rb_define_const(mPng, "INFO_pCAL", INT2FIX(PNG_INFO_pCAL));
  rb_define_const(mPng, "INFO_sRGB", INT2FIX(PNG_INFO_sRGB));
  rb_define_const(mPng, "INFO_iCCP", INT2FIX(PNG_INFO_iCCP));
  rb_define_const(mPng, "INFO_sPLT", INT2FIX(PNG_INFO_sPLT));
  rb_define_const(mPng, "INFO_sCAL", INT2FIX(PNG_INFO_sCAL));
  rb_define_const(mPng, "INFO_IDAT", INT2FIX(PNG_INFO_IDAT));

    /* Transform masks */
  rb_define_const(mPng, "TRANSFORM_IDENTITY",     INT2FIX(PNG_TRANSFORM_IDENTITY));
  rb_define_const(mPng, "TRANSFORM_STRIP_16",     INT2FIX(PNG_TRANSFORM_STRIP_16));
  rb_define_const(mPng, "TRANSFORM_STRIP_ALPHA",  INT2FIX(PNG_TRANSFORM_STRIP_ALPHA));
  rb_define_const(mPng, "TRANSFORM_PACKING",      INT2FIX(PNG_TRANSFORM_PACKING));
  rb_define_const(mPng, "TRANSFORM_PACKSWAP",     INT2FIX(PNG_TRANSFORM_PACKSWAP));
  rb_define_const(mPng, "TRANSFORM_EXPAND",       INT2FIX(PNG_TRANSFORM_EXPAND));
  rb_define_const(mPng, "TRANSFORM_INVERT_MONO",  INT2FIX(PNG_TRANSFORM_INVERT_MONO));
  rb_define_const(mPng, "TRANSFORM_SHIFT",        INT2FIX(PNG_TRANSFORM_SHIFT));
  rb_define_const(mPng, "TRANSFORM_BGR",          INT2FIX(PNG_TRANSFORM_BGR));
  rb_define_const(mPng, "TRANSFORM_SWAP_ALPHA",   INT2FIX(PNG_TRANSFORM_SWAP_ALPHA));
  rb_define_const(mPng, "TRANSFORM_SWAP_ENDIAN",  INT2FIX(PNG_TRANSFORM_SWAP_ENDIAN));
  rb_define_const(mPng, "TRANSFORM_INVERT_ALPHA", INT2FIX(PNG_TRANSFORM_INVERT_ALPHA));
  rb_define_const(mPng, "TRANSFORM_STRIP_FILLER", INT2FIX(PNG_TRANSFORM_STRIP_FILLER));

#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
  rb_define_const(mPng, "FILLER_BEFORE", INT2FIX(PNG_FILLER_BEFORE));
  rb_define_const(mPng, "FILLER_AFTER",  INT2FIX(PNG_FILLER_AFTER));
#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */

#if defined(PNG_READ_BACKGROUND_SUPPORTED)
  rb_define_const(mPng, "BACKGROUND_GAMMA_UNKNOWN", INT2FIX(PNG_BACKGROUND_GAMMA_UNKNOWN));
  rb_define_const(mPng, "BACKGROUND_GAMMA_FILE",    INT2FIX(PNG_BACKGROUND_GAMMA_FILE));
  rb_define_const(mPng, "BACKGROUND_GAMMA_UNIQUE",  INT2FIX(PNG_BACKGROUND_GAMMA_UNIQUE));
#endif /* PNG_READ_BACKGROUND_SUPPORTED */

  rb_define_const(mPng, "CRC_DEFAULT",      INT2FIX(PNG_CRC_DEFAULT));
  rb_define_const(mPng, "CRC_ERROR_QUIT",   INT2FIX(PNG_CRC_ERROR_QUIT));
  rb_define_const(mPng, "CRC_WARN_DISCARD", INT2FIX(PNG_CRC_WARN_DISCARD));
  rb_define_const(mPng, "CRC_WARN_USE",     INT2FIX(PNG_CRC_WARN_USE));
  rb_define_const(mPng, "CRC_QUIET_USE",    INT2FIX(PNG_CRC_QUIET_USE));
  rb_define_const(mPng, "CRC_NO_CHANGE",    INT2FIX(PNG_CRC_NO_CHANGE));

  rb_define_const(mPng, "NO_FILTERS",   INT2FIX(PNG_NO_FILTERS));
  rb_define_const(mPng, "FILTER_NONE",  INT2FIX(PNG_FILTER_NONE));
  rb_define_const(mPng, "FILTER_SUB",   INT2FIX(PNG_FILTER_SUB));
  rb_define_const(mPng, "FILTER_UP",    INT2FIX(PNG_FILTER_UP));
  rb_define_const(mPng, "FILTER_AVG",   INT2FIX(PNG_FILTER_AVG));
  rb_define_const(mPng, "FILTER_PAETH", INT2FIX(PNG_FILTER_PAETH));
  rb_define_const(mPng, "ALL_FILTERS",  INT2FIX(PNG_ALL_FILTERS));

  rb_define_const(mPng, "FILTER_HEURISTIC_DEFAULT",    INT2FIX(PNG_FILTER_HEURISTIC_DEFAULT));
  rb_define_const(mPng, "FILTER_HEURISTIC_UNWEIGHTED", INT2FIX(PNG_FILTER_HEURISTIC_UNWEIGHTED));

  rb_define_const(mPng, "HANDLE_CHUNK_AS_DEFAULT", INT2FIX(HANDLE_CHUNK_AS_DEFAULT));
  rb_define_const(mPng, "HANDLE_CHUNK_NEVER",      INT2FIX(HANDLE_CHUNK_NEVER));
  rb_define_const(mPng, "HANDLE_CHUNK_IF_SAFE",    INT2FIX(HANDLE_CHUNK_IF_SAFE));
  rb_define_const(mPng, "HANDLE_CHUNK_ALWAYS",     INT2FIX(HANDLE_CHUNK_ALWAYS));


  /* define module functions */

  rb_define_module_function(mPng, "copyright",      libpng_get_copyright,      0);
  rb_define_module_function(mPng, "is_png?",        libpng_is_png_p,           1);
  rb_define_module_function(mPng, "libpng_ver",     libpng_get_libpng_ver,     0);
  rb_define_module_function(mPng, "header_ver",     libpng_get_header_ver,     0);
  rb_define_module_function(mPng, "header_version", libpng_get_header_version, 0);


  /* define methods */

  rb_define_method(mPng, "initialize", libpng_initialize, -1);
}
