/**
 * Graphics::PNG is PNG library
 *
 * text.c includes one class:
 *   Graphics::PNG::Text.
 * This class is wrapper object for libpng's png_text_struct struct.
 * It's for the representation of text chunks.
 *
 * @author DATE Ken (as Itacchi) / ge6537@i.bekkoame.ne.jp
 * @code-name Aoi
 * @see http://www.isc.meiji.ac.jp/~ee77038/ruby/
 * $Id: text.c,v 1.1 2000/09/27 17:17:03 date Exp date $
 */

#include "libpng.h"

VALUE cText;


/*
 * -------------------
 * Graphics::PNG::Text
 * -------------------
 * is a wrapper object of the libpng's png_text_struct struct.
 */
/*
=begin
= (({Graphics::PNG::Text}))
A wrapper object of the libpng's png_text_struct struct.


=== class method

--- Graphics::PNG::Text.new
    Create PNG text data for text chunk object.


=== Constant
    : ((<(({Graphics::PNG::TEXT_COMPRESSION_NONE_WR}))>))
    : ((<(({Graphics::PNG::TEXT_COMPRESSION_zTXt_WR}))>))
    : ((<(({Graphics::PNG::TEXT_COMPRESSION_NONE}))>))
    : ((<(({Graphics::PNG::TEXT_COMPRESSION_zTXt}))>))
    : ((<(({Graphics::PNG::ITXT_COMPRESSION_NONE}))>))
    : ((<(({Graphics::PNG::ITXT_COMPRESSION_zTXt}))>))


=== instance method

--- Graphics::PNG::Text#compression
    Get compression type.
    Default is Graphics::PNG::TEXT_COMPRESSION_NONE.

    * ((<(({Graphics::PNG::TEXT_COMPRESSION_NONE_WR}))>))
       -3, tEXt, none, wide character

    * ((<(({Graphics::PNG::TEXT_COMPRESSION_zTXt_WR}))>))
       -2, zTXt, deflate, wide character

    * ((<(({Graphics::PNG::TEXT_COMPRESSION_NONE}))>))
       -1, tEXt, none
    
    * ((<(({Graphics::PNG::TEXT_COMPRESSION_zTXt}))>))
       0, zTXt, deflate,
    
    * ((<(({Graphics::PNG::ITXT_COMPRESSION_NONE}))>))
       1, iTXt, none

    * ((<(({Graphics::PNG::ITXT_COMPRESSION_zTXt}))>))
       2, iTXt, deflate


--- Graphics::PNG::Text#compression=(val)
    Set compression type.

--- Graphics::PNG::Text#key
    Get keyword (description of "text").
    1-79 character.

--- Graphics::PNG::Text#key=(string)
    Set keyword, 1-79 Latin-1 character.
    But, don't check character code.
    This is task for users.

--- Graphics::PNG::Text#lang
    Get language code.

--- Graphics::PNG::Text#lang=(str)
    Set language code, 1-79 characters.
    `nil' for unknown.
    But, don't check character code.
    This is task for users.

--- Graphics::PNG::Text#lang_key
    Get keyword translated UTF-8 string.

--- Graphics::PNG::Text#lang_key=(str)
    Set keyword translated UTF-8 string.
    But, don't check character code.
    This is task for users.

--- Graphics::PNG::Text#text
    Get comment.

--- Graphics::PNG::Text#text=(str)
    Set comment. Empty string is also valid.
=end
*/


#ifdef PNG_TEXT_SUPPORTED
/*
 * ---------------------
 * utility function for
 *   Graphics::PNG::Text
 * ---------------------
 */
void
libpng_text_check_type(obj)
  VALUE obj;
{
  if (rb_class_of(obj) != cText){
    rb_raise(rb_eTypeError,
             "wrong argument type %s (expected Graphics::PNG::Text)",
             rb_class2name(CLASS_OF(obj)));
  }
}



static void
libpng_text_free(text)
  png_textp text; 
{
  if (text->key) free(text->key);
  if (text->text) free(text->text);
#ifdef PNG_iTXt_SUPPORTED
  if (text->lang) free(text->lang);
  if (text->lang_key) free(text->lang_key);
#endif /* PNG_iTXt_SUPPORTED */
  free(text);
}




/*
 * ---------------------
 * class method of
 *   Graphics::PNG::Text
 * ---------------------
 */
static VALUE
libpng_text_new(klass)
  VALUE klass;
{
  VALUE new_obj;
  png_textp text;

  new_obj = Data_Make_Struct(klass, png_text, NULL, libpng_text_free, text);

  text->key  = NULL;
  text->text = NULL;
  text->compression = PNG_TEXT_COMPRESSION_NONE;
#ifdef PNG_iTXt_SUPPORTED
  text->lang     = NULL;
  text->lang_key = NULL;
#endif /* PNG_iTXt_SUPPORTED */

  return new_obj;
}



VALUE
libpng_text_new2(klass, png_text_src)
  VALUE klass;
  png_textp png_text_src;
{
  VALUE new_obj;
  png_textp text;

  new_obj = Data_Make_Struct(klass, png_text, NULL, free, text);

  if (png_text_src->key != NULL){
    text->key = ALLOC_N(char, strlen(png_text_src->key)+1);
    strcpy(text->key, png_text_src->key);
  }

  if (png_text_src->text != NULL){
    text->text = ALLOC_N(char, strlen(png_text_src->text)+1);
    strcpy(text->text, png_text_src->text);
  }

  text->compression = png_text_src->compression;

#ifdef PNG_iTXt_SUPPORTED
  if (png_text_src->lang != NULL){
    text->lang = ALLOC_N(char, strlen(png_text_src->lang)+1);
    strcpy(text->lang, png_text_src->lang);
  }

  if (png_text_src->lang_key != NULL){
    text->lang_key = ALLOC_N(char, strlen(png_text_src->lang_key)+1);
    strcpy(text->lang_key, png_text_src->lang_key);
  }
#endif /* PNG_iTXt_SUPPORTED */

  return new_obj;
}




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



static VALUE
libpng_text_get_compression(obj)
  VALUE obj;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  return INT2FIX(text->compression);
}



static VALUE
libpng_text_get_key(obj)
  VALUE obj;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->key)
    return rb_str_new2(text->key);

  return Qnil;
}



#ifdef PNG_iTXt_SUPPORTED
static VALUE
libpng_text_get_lang(obj)
  VALUE obj;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->lang)
    return rb_str_new2(text->lang);

  return Qnil;
}



static VALUE
libpng_text_get_lang_key(obj)
  VALUE obj;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->lang_key)
    return rb_str_new2(text->lang_key);

  return Qnil;
}
#endif /* PNG_iTXt_SUPPORTED */



static VALUE
libpng_text_get_text(obj)
  VALUE obj;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->text)
    return rb_str_new2(text->text);

  return Qnil;
}



static VALUE
libpng_text_inspect(obj)
  VALUE obj;
{
  png_textp text;
  char *buf;
  VALUE str;
  int text_len = 0;

  Data_Get_Struct(obj, png_text, text);

  if (text->text)
    text_len = strlen(text->text);

  if (text_len < 256)
    text_len = 256;

  buf = ALLOCA_N(char, text_len);

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

  sprintf(buf, "@key=\"%s\", ", text->key);
  rb_str_cat(str, buf, strlen(buf));

  sprintf(buf, "@text=\"%s\", ", text->text);
  rb_str_cat(str, buf, strlen(buf));

  switch (text->compression){
    case PNG_TEXT_COMPRESSION_NONE_WR:
      strcpy(buf, "@compression=Graphics::PNG::TEXT_COMPRESSION_NONE_WR");
      break;

    case PNG_TEXT_COMPRESSION_zTXt_WR:
      strcpy(buf, "@compression=Graphics::PNG::TEXT_COMPRESSION_zTXt_WR");
      break;

    case PNG_TEXT_COMPRESSION_NONE:
      strcpy(buf, "@compression=Graphics::PNG::TEXT_COMPRESSION_NONE");
      break;

    case PNG_TEXT_COMPRESSION_zTXt:
      strcpy(buf, "@compression=Graphics::PNG::TEXT_COMPRESSION_zTXt");
      break;

    case PNG_ITXT_COMPRESSION_NONE:
      strcpy(buf, "@compression=Graphics::PNG::ITXT_COMPRESSION_NONE");
      break;

    case PNG_ITXT_COMPRESSION_zTXt:
      strcpy(buf, "@compression=Graphics::PNG::ITXT_COMPRESSION_zTXt");
      break;

    default:
      strcpy(buf, "@compression=unkown type");
  }
  rb_str_cat(str, buf, strlen(buf));


#ifdef PNG_iTXt_SUPPORTED
  rb_str_cat(str, ", ", 2);
  if (text->lang)
    sprintf(buf, "@lang=\"%s\", ", text->lang);
  else
    strcpy(buf, "@lang=unknown, ");

  rb_str_cat(str, buf, strlen(buf));


  if (text->lang_key)
    sprintf(buf, "@lang_key=\"%s\"", text->lang_key);
  else
    strcpy(buf, "@lang_key=unknown");

  rb_str_cat(str, buf, strlen(buf));
#endif /* PNG_iTXt_SUPPORTED */

  rb_str_cat(str, ">", 1);

  return str;
}



static VALUE
libpng_text_set_compression(obj, val)
  VALUE obj, val;
{
  png_textp text;
  int compression_type;

  FIXNUM_P(val);

  compression_type = FIX2INT(val);

  if (compression_type < PNG_TEXT_COMPRESSION_NONE_WR ||
      compression_type >= PNG_TEXT_COMPRESSION_LAST){
    rb_raise(ePngError,
             "invalid value for compression type of text chunk: %d",
             compression_type);
  }

  Data_Get_Struct(obj, png_text, text);

  text->compression = compression_type;

  return Qnil;
}



static VALUE
libpng_text_set_key(obj, str)
  VALUE obj, str;
{
  png_textp text;

  Check_Type(str, T_STRING);

  Data_Get_Struct(obj, png_text, text);

  if (text->key) free(text->key);

  if (RSTRING(str)->len > 79){
    rb_warning("Given string is too long.");
    text->key = ALLOC_N(char, 80);
    strncpy(text->key, STR2CSTR(str), 79);
  }
  else {
    text->key = ALLOC_N(char, RSTRING(str)->len+1);
    strcpy(text->key, STR2CSTR(str));
  }

  return Qnil;
}



#ifdef PNG_iTXt_SUPPORTED
static VALUE
libpng_text_set_lang(obj, str)
  VALUE obj, str;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->lang) free(text->lang);

  switch (TYPE(str)){
    case T_NIL:
      text->lang = NULL;
      break;

    case T_STRING:
      if (RSTRING(str)->len > 79){
        rb_warning("Given string is too long.");
        text->lang = ALLOC_N(char, 80);
        strncpy(text->lang, STR2CSTR(str), 79);
      }
      else {
        text->lang = ALLOC_N(char, RSTRING(str)->len+1);
        strcpy(text->lang, STR2CSTR(str));
      }
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected String/nil)",
               rb_class2name(CLASS_OF(str)));
  }

  return Qnil;
}



static VALUE
libpng_text_set_lang_key(obj, str)
  VALUE obj, str;
{
  png_textp text;

  Data_Get_Struct(obj, png_text, text);

  if (text->lang_key) free(text->lang_key);

  switch (TYPE(str)){
    case T_NIL:
      text->lang_key = NULL;
      break;

    case T_STRING:
      text->lang_key = ALLOC_N(char, RSTRING(str)->len+1);
      strcpy(text->lang_key, STR2CSTR(str));
      break;

    default:
      rb_raise(rb_eTypeError, "wrong argument type %s (expected String/nil)",
               rb_class2name(CLASS_OF(str)));
  }

  return Qnil;
}
#endif /* PNG_iTXt_SUPPORTED */



static VALUE
libpng_text_set_text(obj, str)
  VALUE obj, str;
{
  png_textp text;

  Check_Type(str, T_STRING);

  Data_Get_Struct(obj, png_text, text);

  if (text->text) free(text->text);

  text->text = ALLOC_N(char, RSTRING(str)->len+1);
  strcpy(text->text, STR2CSTR(str));

  return Qnil;
}
#endif /* PNG_TEXT_SUPPORTED */




void
Init_text()
{
  /*
   * -------------------
   * Graphics::PNG::Text
   * -------------------
   */

#if defined(PNG_TEXT_SUPPORTED)
  cText = rb_define_class_under(mPng, "Text", rb_cObject);


  rb_define_const(mPng, "TEXT_COMPRESSION_NONE_WR", INT2FIX(PNG_TEXT_COMPRESSION_NONE_WR));
  rb_define_const(mPng, "TEXT_COMPRESSION_zTXt_WR", INT2FIX(PNG_TEXT_COMPRESSION_zTXt_WR));
  rb_define_const(mPng, "TEXT_COMPRESSION_NONE",    INT2FIX(PNG_TEXT_COMPRESSION_NONE));
  rb_define_const(mPng, "TEXT_COMPRESSION_zTXt",    INT2FIX(PNG_TEXT_COMPRESSION_zTXt));
  rb_define_const(mPng, "ITXT_COMPRESSION_NONE",    INT2FIX(PNG_ITXT_COMPRESSION_NONE));
  rb_define_const(mPng, "ITXT_COMPRESSION_zTXt",    INT2FIX(PNG_ITXT_COMPRESSION_zTXt));


  rb_define_singleton_method(cText, "new", libpng_text_new, 0);


  rb_define_method(cText, "initialize", libpng_text_initialize, -1);

  rb_define_method(cText, "compression", libpng_text_get_compression, 0);
  rb_define_method(cText, "key",         libpng_text_get_key,         0);
#ifdef PNG_iTXt_SUPPORTED
  rb_define_method(cText, "lang",        libpng_text_get_lang,        0);
  rb_define_method(cText, "lang_key",    libpng_text_get_lang_key,    0);
#endif /* PNG_iTXt_SUPPORTED */
  rb_define_method(cText, "text",        libpng_text_get_text,        0);

  rb_define_method(cText, "inspect", libpng_text_inspect, 0);

  rb_define_method(cText, "compression=", libpng_text_set_compression, 1);
  rb_define_method(cText, "key=",         libpng_text_set_key,         1);
#ifdef PNG_iTXt_SUPPORTED
  rb_define_method(cText, "lang=",        libpng_text_set_lang,        1);
  rb_define_method(cText, "lang_key=",    libpng_text_set_lang_key,    1);
#endif /* PNG_iTXt_SUPPORTED */
  rb_define_method(cText, "text=",        libpng_text_set_text,        1);
#endif /* PNG_TEXT_SUPPORTED */
}
