
#include <IL/il.h>
#include <ruby.h>

#define DEVIL_VERSION "0.1.0"
#define UNUSED(a) ((void) (a))
#define DEF_CONST(a,b)                         \
  do {                                   \
    VALUE v = INT2FIX(b);                \
    rb_define_const(mDevil, a, v);       \
    rb_define_const(mDevil, "IL_" a, v); \
  } while (0)

#define DEF_METH(a,b,c,d,e)              \
  do {                                   \
    rb_define_singleton_method(a,b,d,e); \
    rb_define_singleton_method(a,c,d,e); \
  } while (0)

static VALUE mDevil;

static VALUE il_active_im(VALUE self, VALUE num) {
  return ilActiveImage(NUM2INT(num)) ? Qtrue : Qfalse;
}

static VALUE il_active_layer(VALUE self, VALUE num) {
  return ilActiveLayerr(NUM2INT(num)) ? Qtrue : Qfalse;
}

static VALUE il_active_mipmap(VALUE self, VALUE num) {
  return ilActiveMipmap(NUM2INT(num)) ? Qtrue : Qfalse;
}

static VALUE il_apply_pal(VALUE self, VALUE path) {
  return ilApplyPal(RSTRING(path)->ptr) ? Qtrue : Qfalse;
}

static VALUE il_apply_profile(VALUE self, VALUE in, VALUE out) {
  return ilApplyProfile(RSTRING(in)->ptr, RSTRING(out)->ptr) ? Qtrue : Qfalse;
}

static void define_constants(void) {
  DEF_CONST("COLOUR_INDEX", IL_COLOUR_INDEX);
  DEF_CONST("COLOR_INDEX", IL_COLOR_INDEX);
  DEF_CONST("RGB", IL_RGB);
  DEF_CONST("RGBA", IL_RGBA);
  DEF_CONST("BGR", IL_BGR);
  DEF_CONST("BGRA", IL_BGRA);
  DEF_CONST("LUMINANCE", IL_LUMINANCE);
  DEF_CONST("LUMINANCE_ALPHA", IL_LUMINANCE_ALPHA);

  DEF_CONST("BYTE", IL_BYTE);
  DEF_CONST("UNSIGNED_BYTE", IL_UNSIGNED_BYTE);
  DEF_CONST("SHORT", IL_SHORT);
  DEF_CONST("UNSIGNED_SHORT", IL_UNSIGNED_SHORT);
  DEF_CONST("INT", IL_INT);
  DEF_CONST("UNSIGNED_INT", IL_UNSIGNED_INT);
  DEF_CONST("FLOAT", IL_FLOAT);
  DEF_CONST("DOUBLE", IL_DOUBLE);

  DEF_CONST("VENDOR", IL_VENDOR);
  DEF_CONST("LOAD_EXT", IL_LOAD_EXT);
  DEF_CONST("SAVE_EXT", IL_SAVE_EXT);

  /* IL-specific #define's */
  DEF_CONST("VERSION_1_6_1", IL_VERSION_1_6_1);
  DEF_CONST("VERSION", IL_VERSION);

  /* Attribute Bits */
  DEF_CONST("ORIGIN_BIT", IL_ORIGIN_BIT);
  DEF_CONST("FILE_BIT", IL_FILE_BIT);
  DEF_CONST("PAL_BIT", IL_PAL_BIT);
  DEF_CONST("FORMAT_BIT", IL_FORMAT_BIT);
  DEF_CONST("TYPE_BIT", IL_TYPE_BIT);
  DEF_CONST("COMPRESS_BIT", IL_COMPRESS_BIT);
  DEF_CONST("LOADFAIL_BIT", IL_LOADFAIL_BIT);
  DEF_CONST("FORMAT_SPECIFIC_BIT", IL_FORMAT_SPECIFIC_BIT);
  DEF_CONST("ALL_ATTRIB_BITS", IL_ALL_ATTRIB_BITS);

  /* Palette types */
  DEF_CONST("PAL_NONE", IL_PAL_NONE);
  DEF_CONST("PAL_RGB24", IL_PAL_RGB24);
  DEF_CONST("PAL_RGB32", IL_PAL_RGB32);
  DEF_CONST("PAL_RGBA32", IL_PAL_RGBA32);
  DEF_CONST("PAL_BGR24", IL_PAL_BGR24);
  DEF_CONST("PAL_BGR32", IL_PAL_BGR32);
  DEF_CONST("PAL_BGRA32", IL_PAL_BGRA32);

  /* Image types */
  DEF_CONST("TYPE_UNKNOWN", IL_TYPE_UNKNOWN);
  DEF_CONST("BMP", IL_BMP);
  DEF_CONST("CUT", IL_CUT);
  DEF_CONST("DOOM", IL_DOOM);
  DEF_CONST("DOOM_FLAT", IL_DOOM_FLAT);
  DEF_CONST("ICO", IL_ICO);
  DEF_CONST("JPG", IL_JPG);
  DEF_CONST("JFIF", IL_JFIF);
  DEF_CONST("LBM", IL_LBM);
  DEF_CONST("PCD", IL_PCD);
  DEF_CONST("PCX", IL_PCX);
  DEF_CONST("PIC", IL_PIC);
  DEF_CONST("PNG", IL_PNG);
  DEF_CONST("PNM", IL_PNM);
  DEF_CONST("SGI", IL_SGI);
  DEF_CONST("TGA", IL_TGA);
  DEF_CONST("TIF", IL_TIF);
  DEF_CONST("CHEAD", IL_CHEAD);
  DEF_CONST("RAW", IL_RAW);
  DEF_CONST("MDL", IL_MDL);
  DEF_CONST("WAL", IL_WAL);
  DEF_CONST("LIF", IL_LIF);
  DEF_CONST("MNG", IL_MNG);
  DEF_CONST("JNG", IL_JNG);
  DEF_CONST("GIF", IL_GIF);
  DEF_CONST("DDS", IL_DDS);
  DEF_CONST("DCX", IL_DCX);
  DEF_CONST("PSD", IL_PSD);
  DEF_CONST("EXIF", IL_EXIF);
  DEF_CONST("PSP", IL_PSP);
  DEF_CONST("PIX", IL_PIX);
  DEF_CONST("PXR", IL_PXR);
  DEF_CONST("XPM", IL_XPM);

  DEF_CONST("JASC_PAL", IL_JASC_PAL);

  /* Error Types */
  DEF_CONST("NO_ERROR", IL_NO_ERROR);
  DEF_CONST("INVALID_ENUM", IL_INVALID_ENUM);
  DEF_CONST("OUT_OF_MEMORY", IL_OUT_OF_MEMORY);
  DEF_CONST("FORMAT_NOT_SUPPORTED", IL_FORMAT_NOT_SUPPORTED);
  DEF_CONST("INTERNAL_ERROR", IL_INTERNAL_ERROR);
  DEF_CONST("INVALID_VALUE", IL_INVALID_VALUE);
  DEF_CONST("ILLEGAL_OPERATION", IL_ILLEGAL_OPERATION);
  DEF_CONST("ILLEGAL_FILE_VALUE", IL_ILLEGAL_FILE_VALUE);
  DEF_CONST("INVALID_FILE_HEADER", IL_INVALID_FILE_HEADER);
  DEF_CONST("INVALID_PARAM", IL_INVALID_PARAM);
  DEF_CONST("COULD_NOT_OPEN_FILE", IL_COULD_NOT_OPEN_FILE);
  DEF_CONST("INVALID_EXTENSION", IL_INVALID_EXTENSION);
  DEF_CONST("FILE_ALREADY_EXISTS", IL_FILE_ALREADY_EXISTS);
  DEF_CONST("OUT_FORMAT_SAME", IL_OUT_FORMAT_SAME);
  DEF_CONST("STACK_OVERFLOW", IL_STACK_OVERFLOW);
  DEF_CONST("STACK_UNDERFLOW", IL_STACK_UNDERFLOW);
  DEF_CONST("INVALID_CONVERSION", IL_INVALID_CONVERSION);
  DEF_CONST("BAD_DIMENSIONS", IL_BAD_DIMENSIONS);
  DEF_CONST("FILE_READ_ERROR", IL_FILE_READ_ERROR);
  DEF_CONST("FILE_WRITE_ERROR", IL_FILE_WRITE_ERROR);

  DEF_CONST("LIB_GIF_ERROR", IL_LIB_GIF_ERROR);
  DEF_CONST("LIB_JPEG_ERROR", IL_LIB_JPEG_ERROR);
  DEF_CONST("LIB_PNG_ERROR", IL_LIB_PNG_ERROR);
  DEF_CONST("LIB_TIFF_ERROR", IL_LIB_TIFF_ERROR);
  DEF_CONST("LIB_MNG_ERROR", IL_LIB_MNG_ERROR);
  DEF_CONST("UNKNOWN_ERROR", IL_UNKNOWN_ERROR);

  /* Origin Definitions */
  DEF_CONST("ORIGIN_SET", IL_ORIGIN_SET);
  DEF_CONST("ORIGIN_LOWER_LEFT", IL_ORIGIN_LOWER_LEFT);
  DEF_CONST("ORIGIN_UPPER_LEFT", IL_ORIGIN_UPPER_LEFT);
  DEF_CONST("ORIGIN_MODE", IL_ORIGIN_MODE);

  /* Format and Type Mode Definitions */
  DEF_CONST("FORMAT_SET", IL_FORMAT_SET);
  DEF_CONST("FORMAT_MODE", IL_FORMAT_MODE);
  DEF_CONST("TYPE_SET", IL_TYPE_SET);
  DEF_CONST("TYPE_MODE", IL_TYPE_MODE);

  /* File definitions */
  DEF_CONST("FILE_OVERWRITE", IL_FILE_OVERWRITE);
  DEF_CONST("FILE_MODE", IL_FILE_MODE);

  /* Palette definitions */
  DEF_CONST("CONV_PAL", IL_CONV_PAL);

  /* Load fail definitions */
  DEF_CONST("DEFAULT_ON_FAIL", IL_DEFAULT_ON_FAIL);

  /* Key colour definitions */
  DEF_CONST("USE_KEY_COLOUR", IL_USE_KEY_COLOUR);
  DEF_CONST("USE_KEY_COLOR", IL_USE_KEY_COLOR);

  /* Interlace definitions */
  DEF_CONST("SAVE_INTERLACED", IL_SAVE_INTERLACED);
  DEF_CONST("INTERLACE_MODE", IL_INTERLACE_MODE);

  /* Quantization definitions */
  DEF_CONST("QUANTIZATION_MODE", IL_QUANTIZATION_MODE);
  DEF_CONST("WU_QUANT", IL_WU_QUANT);
  DEF_CONST("NEU_QUANT", IL_NEU_QUANT);
  DEF_CONST("NEU_QUANT_SAMPLE", IL_NEU_QUANT_SAMPLE);

  /* Hints */
  DEF_CONST("FASTEST", IL_FASTEST);
  DEF_CONST("LESS_MEM", IL_LESS_MEM);
  DEF_CONST("DONT_CARE", IL_DONT_CARE);
  DEF_CONST("MEM_SPEED_HINT", IL_MEM_SPEED_HINT);
  DEF_CONST("USE_COMPRESSION", IL_USE_COMPRESSION);
  DEF_CONST("NO_COMPRESSION", IL_NO_COMPRESSION);
  DEF_CONST("COMPRESSION_HINT", IL_COMPRESSION_HINT);
  
  /* Subimage types */
  DEF_CONST("SUB_NEXT", IL_SUB_NEXT);
  DEF_CONST("SUB_MIPMAP", IL_SUB_MIPMAP);
  DEF_CONST("SUB_LAYER", IL_SUB_LAYER);

  /* Compression definitions */
  DEF_CONST("COMPRESS_MODE", IL_COMPRESS_MODE);
  DEF_CONST("COMPRESS_NONE", IL_COMPRESS_NONE);
  DEF_CONST("COMPRESS_RLE", IL_COMPRESS_RLE);
  DEF_CONST("COMPRESS_LZO", IL_COMPRESS_LZO);
  DEF_CONST("COMPRESS_ZLIB", IL_COMPRESS_ZLIB);

  /* File format-specific values */
  DEF_CONST("TGA_CREATE_STAMP", IL_TGA_CREATE_STAMP);
  DEF_CONST("JPG_QUALITY", IL_JPG_QUALITY);
  DEF_CONST("PNG_INTERLACE", IL_PNG_INTERLACE);
  DEF_CONST("TGA_RLE", IL_TGA_RLE);
  DEF_CONST("BMP_RLE", IL_BMP_RLE);
  DEF_CONST("SGI_RLE", IL_SGI_RLE);
  DEF_CONST("TGA_ID_STRING", IL_TGA_ID_STRING);
  DEF_CONST("TGA_AUTHNAME_STRING", IL_TGA_AUTHNAME_STRING);
  DEF_CONST("TGA_AUTHCOMMENT_STRING", IL_TGA_AUTHCOMMENT_STRING);
  DEF_CONST("PNG_AUTHNAME_STRING", IL_PNG_AUTHNAME_STRING);
  DEF_CONST("PNG_TITLE_STRING", IL_PNG_TITLE_STRING);
  DEF_CONST("PNG_DESCRIPTION_STRING", IL_PNG_DESCRIPTION_STRING);
  DEF_CONST("TIF_DESCRIPTION_STRING", IL_TIF_DESCRIPTION_STRING);
  DEF_CONST("TIF_HOSTCOMPUTER_STRING", IL_TIF_HOSTCOMPUTER_STRING);
  DEF_CONST("TIF_DOCUMENTNAME_STRING", IL_TIF_DOCUMENTNAME_STRING);
  DEF_CONST("TIF_AUTHNAME_STRING", IL_TIF_AUTHNAME_STRING);
  DEF_CONST("JPG_SAVE_FORMAT", IL_JPG_SAVE_FORMAT);
  DEF_CONST("CHEAD_HEADER_STRING", IL_CHEAD_HEADER_STRING);
  DEF_CONST("PCD_PICNUM", IL_PCD_PICNUM);

  /* DXTC definitions */
  DEF_CONST("DXTC_FORMAT", IL_DXTC_FORMAT);
  DEF_CONST("DXT1", IL_DXT1);
  DEF_CONST("DXT2", IL_DXT2);
  DEF_CONST("DXT3", IL_DXT3);
  DEF_CONST("DXT4", IL_DXT4);
  DEF_CONST("DXT5", IL_DXT5);
  DEF_CONST("DXT_NO_COMP", IL_DXT_NO_COMP);
  DEF_CONST("KEEP_DXTC_DATA", IL_KEEP_DXTC_DATA);
  DEF_CONST("DXTC_DATA_FORMAT", IL_DXTC_DATA_FORMAT);

  /* Cube map definitions */
  DEF_CONST("CUBEMAP_POSITIVEX", IL_CUBEMAP_POSITIVEX);
  DEF_CONST("CUBEMAP_NEGATIVEX", IL_CUBEMAP_NEGATIVEX);
  DEF_CONST("CUBEMAP_POSITIVEY", IL_CUBEMAP_POSITIVEY);
  DEF_CONST("CUBEMAP_NEGATIVEY", IL_CUBEMAP_NEGATIVEY);
  DEF_CONST("CUBEMAP_POSITIVEZ", IL_CUBEMAP_POSITIVEZ);
  DEF_CONST("CUBEMAP_NEGATIVEZ", IL_CUBEMAP_NEGATIVEZ);

  /* Values */
  DEF_CONST("VERSION_NUM", IL_VERSION_NUM);
  DEF_CONST("IMAGE_WIDTH", IL_IMAGE_WIDTH);
  DEF_CONST("IMAGE_HEIGHT", IL_IMAGE_HEIGHT);
  DEF_CONST("IMAGE_DEPTH", IL_IMAGE_DEPTH);
  DEF_CONST("IMAGE_SIZE_OF_DATA", IL_IMAGE_SIZE_OF_DATA);
  DEF_CONST("IMAGE_BPP", IL_IMAGE_BPP);
  DEF_CONST("IMAGE_BYTES_PER_PIXEL", IL_IMAGE_BYTES_PER_PIXEL);
  DEF_CONST("IMAGE_BITS_PER_PIXEL", IL_IMAGE_BITS_PER_PIXEL);
  DEF_CONST("IMAGE_FORMAT", IL_IMAGE_FORMAT);
  DEF_CONST("IMAGE_TYPE", IL_IMAGE_TYPE);
  DEF_CONST("PALETTE_TYPE", IL_PALETTE_TYPE);
  DEF_CONST("PALETTE_SIZE", IL_PALETTE_SIZE);
  DEF_CONST("PALETTE_BPP", IL_PALETTE_BPP);
  DEF_CONST("PALETTE_NUM_COLS", IL_PALETTE_NUM_COLS);
  DEF_CONST("PALETTE_BASE_TYPE", IL_PALETTE_BASE_TYPE);
  DEF_CONST("NUM_IMAGES", IL_NUM_IMAGES);
  DEF_CONST("NUM_MIPMAPS", IL_NUM_MIPMAPS);
  DEF_CONST("NUM_LAYERS", IL_NUM_LAYERS);
  DEF_CONST("ACTIVE_IMAGE", IL_ACTIVE_IMAGE);
  DEF_CONST("ACTIVE_MIPMAP", IL_ACTIVE_MIPMAP);
  DEF_CONST("ACTIVE_LAYER", IL_ACTIVE_LAYER);
  DEF_CONST("CUR_IMAGE", IL_CUR_IMAGE);
  DEF_CONST("IMAGE_DURATION", IL_IMAGE_DURATION);
  DEF_CONST("IMAGE_PLANESIZE", IL_IMAGE_PLANESIZE);
  DEF_CONST("IMAGE_BPC", IL_IMAGE_BPC);
  DEF_CONST("IMAGE_OFFX", IL_IMAGE_OFFX);
  DEF_CONST("IMAGE_OFFY", IL_IMAGE_OFFY);
  DEF_CONST("IMAGE_CUBEFLAGS", IL_IMAGE_CUBEFLAGS);
}

void Init_devil(void) {
  mDevil = rb_define_module("DevIL");
  rb_define_const(mDevil, "DEVIL_VERSION", rb_str_new2(DEVIL_VERSION));

  define_constants();

  DEF_METH(mDevil, "active_image", "ActiveImage", il_active_im, 1);
  DEF_METH(mDevil, "active_layer", "ActiveLayer", il_active_layer, 1);
  DEF_METH(mDevil, "active_mipmap", "ActiveMipmap", il_active_mipmap, 1);
  DEF_METH(mDevil, "apply_pal", "ApplyPal", il_apply_pal, 1);
  DEF_METH(mDevil, "apply_profile", "ApplyProfile", il_apply_profile, 2);
  DEF_METH(mDevil, "bind_image", "BindImage", il_bind_image, 1);
  DEF_METH(mDevil, "blit", "Blit", il_blit, 10);
  DEF_METH(mDevil, "clear_color", "ClearColor", il_clear_color, 4);
  DEF_METH(mDevil, "clear_colour", "ClearColour", il_clear_color, 4);
  DEF_METH(mDevil, "clear_image", "ClearImage", il_clear_im, 0);
  DEF_METH(mDevil, "clone_cur_image", "CloneCurImage", il_clone_cur_im, 0);
  DEF_METH(mDevil, "compress_func", "CompressFunc", il_compress_func, 1);
  DEF_METH(mDevil, "convert_image", "ConvertImage", il_convert_im, 2);
  DEF_METH(mDevil, "convert_pal", "ConvertPal", il_convert_pal, 1);
  DEF_METH(mDevil, "copy_image", "CopyImage", il_copy_im, 1);
  DEF_METH(mDevil, "copy_pixels", "CopyPixels", il_copy_pixels, 9);
  DEF_METH(mDevil, "create_sub_image", "CreateSubImage", il_create_sub_im, 2);
  DEF_METH(mDevil, "default_image", "DefaultImage", il_default_im, 0);
  DEF_METH(mDevil, "delete_images", "DeleteImages", il_delete_im, -1);
  DEF_METH(mDevil, "disable", "Disable", il_disable, 1);
  DEF_METH(mDevil, "enable", "Enable", il_enable, 1);
  DEF_METH(mDevil, "format_func", "FormatFunc", il_format_func, 1);
  DEF_METH(mDevil, "gen_images", "GenImages", il_gen_ims, -1);
  DEF_METH(mDevil, "get_alpha", "GetAlpha", il_get_alpha, 1);
  DEF_METH(mDevil, "get_boolean", "GetBoolean", il_get_bool, 1);
  DEF_METH(mDevil, "get_data", "GetData", il_get_data, 0);
  DEF_METH(mDevil, "get_dxtc_data", "GetDXTCData", il_get_dxtc_data, 2);
  DEF_METH(mDevil, "get_error", "GetError", il_get_err, 0);
  DEF_METH(mDevil, "get_integer", "GetInteger", il_get_int, 1);
  DEF_METH(mDevil, "get_lump_pos", "GetLumpPos", il_get_lump_pos, 0);
  DEF_METH(mDevil, "get_palette", "GetPalette", il_get_palette, 0);
  DEF_METH(mDevil, "get_string", "GetString", il_get_string, 1);
  DEF_METH(mDevil, "hint", "Hint", il_hint, 2);
  DEF_METH(mDevil, "is_disabled", "IsDisabled", il_is_disabled, 1);
  DEF_METH(mDevil, "is_disabled?", "IsDisabled?", il_is_disabled, 1);
  DEF_METH(mDevil, "is_enabled", "IsEnabled", il_is_enabled, 1);
  DEF_METH(mDevil, "is_enabled?", "IsEnabled?", il_is_enabled, 1);
  DEF_METH(mDevil, "is_image", "IsImage", il_is_image, 1);
  DEF_METH(mDevil, "is_image?", "IsImage?", il_is_image, 1);
  DEF_METH(mDevil, "is_valid", "IsValid", il_is_valid, 2);
  DEF_METH(mDevil, "is_valid?", "IsValid?", il_is_valid, 2);
  DEF_METH(mDevil, "is_valid_f", "IsValidF", il_is_valid_f, 2);
  DEF_METH(mDevil, "is_valid_f?", "IsValidF?", il_is_valid_f, 2);
  DEF_METH(mDevil, "is_valid_l", "IsValidL", il_is_valid_l, 2);
  DEF_METH(mDevil, "is_valid_l?", "IsValidL?", il_is_valid_l, 2);
  DEF_METH(mDevil, "key_color", "KeyColor", il_key_color, 4);
  DEF_METH(mDevil, "key_colour", "KeyColour", il_key_color, 4);
  DEF_METH(mDevil, "load", "Load", il_load, 2);
  DEF_METH(mDevil, "load_f", "LoadF", il_load_f, 2);
  DEF_METH(mDevil, "load_image", "LoadImage", il_load_im, 1);
  DEF_METH(mDevil, "load_l", "LoadL", il_load_l, 2);
  DEF_METH(mDevil, "load_pal", "LoadPal", il_load_pal, 1);
  DEF_METH(mDevil, "origin_func", "OriginFunc", il_origin_func, 1);
  DEF_METH(mDevil, "overlay_image", "OverlayImage", il_overlay_im, 4);
  DEF_METH(mDevil, "pop_attrib", "PopAttrib", il_pop_attrib, 0);
  DEF_METH(mDevil, "push_attrib", "PushAttrib", il_push_attrib, 1);
  DEF_METH(mDevil, "register_format", "ResisterFormat", il_register_format, 1);
  DEF_METH(mDevil, "register_load", "ResisterLoad", il_register_load, 2);
  DEF_METH(mDevil, "register_mipnum", "ResisterMipnum", il_register_mipnum, 1);
  DEF_METH(mDevil, "register_num_images", "ResisterNumImages", il_register_num_ims, 1);
  DEF_METH(mDevil, "register_origin", "ResisterOrigin", il_register_origin, 1);
  DEF_METH(mDevil, "register_pal", "ResisterPal", il_register_pal, 1);


  /* initialize the DevIL library */
  ilInit();
}
