#ifdef _MSC_VER
#define NO_XSLOCKS // Needed to avoid PerlProc_setjmp/PerlProc_longjmp unresolved symbols
#endif

// On Debian, pngconf.h might complain about setjmp.h being loaded before PNG
// so we have to load png.h first
#ifdef HAVE_PNG
#include <png.h>
#endif

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include "common.c"
#include "image.c"

MODULE = Image::Scale		PACKAGE = Image::Scale

void
__init(HV *self)
PPCODE:
{
  SV *pv = NEWSV(0, sizeof(image));
  image *im = (image *)SvPVX(pv);
  
  SvPOK_only(pv);
  
  if ( !image_init(self, im) ) {
    // Return undef on any errors during header reading
    SvREFCNT_dec(pv);
    XSRETURN_UNDEF;
  }
  
  XPUSHs( sv_2mortal( sv_bless(
    newRV_noinc(pv),
    gv_stashpv("Image::Scale::XS", 1)
  ) ) );
}

int
width(HV *self)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  
  RETVAL = im->width;
}
OUTPUT:
  RETVAL

int
height(HV *self)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  
  RETVAL = im->height;
}
OUTPUT:
  RETVAL

int
resize(HV *self, HV *opts)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  
  // Reset options if resize is being called multiple times
  if (im->target_width) {
    im->target_width  = 0;
    im->target_height = 0;
    im->keep_aspect   = 0;
    im->orientation   = im->orientation_orig;
    im->bgcolor       = 0;
    im->memory_limit  = 0;
    im->resize_type   = IMAGE_SCALE_TYPE_GD;
    im->filter        = 0;
  }
  
  if (my_hv_exists(opts, "width"))
    im->target_width = SvIV(*(my_hv_fetch(opts, "width")));
  
  if (my_hv_exists(opts, "height"))
    im->target_height = SvIV(*(my_hv_fetch(opts, "height")));
  
  if (!im->target_width && !im->target_height) {
    croak("Image::Scale->resize requires at least one of height or width");
  }
  
  if (my_hv_exists(opts, "keep_aspect"))
    im->keep_aspect = SvIV(*(my_hv_fetch(opts, "keep_aspect")));
  
  if (my_hv_exists(opts, "ignore_exif")) {
    if (SvIV(*(my_hv_fetch(opts, "ignore_exif"))) != 0)
      im->orientation = ORIENTATION_NORMAL;
  }
  
  if (my_hv_exists(opts, "bgcolor"))
    im->bgcolor = SvIV(*(my_hv_fetch(opts, "bgcolor"))) << 8 | 0xFF;
  
  if (my_hv_exists(opts, "memory_limit"))
    im->memory_limit = SvIV(*(my_hv_fetch(opts, "memory_limit")));
  
  if (my_hv_exists(opts, "type"))
    im->resize_type = SvIV(*(my_hv_fetch(opts, "type")));
  
  if (my_hv_exists(opts, "filter")) {
    char *filterstr = SvPVX(*(my_hv_fetch(opts, "filter")));
    if (strEQ("Point", filterstr))
      im->filter = PointFilter;
    else if (strEQ("Box", filterstr))
      im->filter = BoxFilter;
    else if (strEQ("Triangle", filterstr))
      im->filter = TriangleFilter;
    else if (strEQ("Hermite", filterstr))
      im->filter = HermiteFilter;
    else if (strEQ("Hanning", filterstr))
      im->filter = HanningFilter;
    else if (strEQ("Hamming", filterstr))
      im->filter = HammingFilter;
    else if (strEQ("Blackman", filterstr))
      im->filter = BlackmanFilter;
    else if (strEQ("Gaussian", filterstr))
      im->filter = GaussianFilter;
    else if (strEQ("Quadratic", filterstr))
      im->filter = QuadraticFilter;
    else if (strEQ("Cubic", filterstr))
      im->filter = CubicFilter;
    else if (strEQ("Catrom", filterstr))
      im->filter = CatromFilter;
    else if (strEQ("Mitchell", filterstr))
      im->filter = MitchellFilter;
    else if (strEQ("Lanczos", filterstr))
      im->filter = LanczosFilter;
    else if (strEQ("Bessel", filterstr))
      im->filter = BesselFilter;
    else if (strEQ("Sinc", filterstr))
      im->filter = SincFilter;
  }
  
  // If the image will be rotated 90 degrees, swap the target values
  if (im->orientation >= 5) {
    if (!im->target_height) {
      // Only width was specified, but this will actually be the target height
      im->target_height = im->target_width;
      im->target_width = 0;
    }
    else if (!im->target_width) {
      // Only height was specified, but this will actually be the target width
      im->target_width = im->target_height;
      im->target_height = 0;
    }
  }      
  
  if (!im->target_height) {
    // Only width was specified
    im->target_height = (int)((float)im->height / im->width * im->target_width);
  }
  else if (!im->target_width) {
    // Only height was specified
    im->target_width = (int)((float)im->width / im->height * im->target_height);
  }
  
  DEBUG_TRACE("Resizing from %d x %d -> %d x %d\n", im->width, im->height, im->target_width, im->target_height);
  
  RETVAL = image_resize(im);
}
OUTPUT:
  RETVAL

#ifdef HAVE_JPEG
void
save_jpeg(HV *self, SV *path, ...)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  int quality = DEFAULT_JPEG_QUALITY;
  
  if ( !SvPOK(path) ) {
    croak("Image::Scale->save_jpeg requires a path");
  }
  
  if (items == 3 && SvOK(ST(2))) {
    quality = SvIV(ST(2));
  }
  
  image_jpeg_save(im, SvPVX(path), quality);
}

SV *
as_jpeg(HV *self, ...)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  int quality = DEFAULT_JPEG_QUALITY;
  
  if (items == 2 && SvOK(ST(1))) {
    quality = SvIV(ST(1));
  }
  
  RETVAL = newSVpvn("", 0);
  
  image_jpeg_to_sv(im, quality, RETVAL);
}
OUTPUT:
  RETVAL

#endif

#ifdef HAVE_PNG
void
save_png(HV *self, SV *path)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
  
  if ( !SvPOK(path) ) {
    croak("Image::Scale->save_jpeg requires a path");
  }
  
  image_png_save(im, SvPVX(path));
}

SV *
as_png(HV *self)
CODE:
{
  image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));

  RETVAL = newSVpvn("", 0);
  
  image_png_to_sv(im, RETVAL);
}
OUTPUT:
  RETVAL

#endif

void
__cleanup(HV *self, image *im)
CODE:
{
  image_finish(im);
}

SV *
jpeg_version(void)
CODE:
{
#ifdef JPEG_VERSION
  RETVAL = newSVpv( STRINGIFY(JPEG_VERSION), 0 );
#else
  RETVAL = &PL_sv_undef;
#endif
}
OUTPUT:
  RETVAL

SV *
png_version(void)
CODE:
{
#ifdef PNG_VERSION
  RETVAL = newSVpv( STRINGIFY(PNG_VERSION), 0 );
#else
  RETVAL = &PL_sv_undef;
#endif
}
OUTPUT:
  RETVAL

SV *
gif_version(void)
CODE:
{
#ifdef GIF_VERSION
  RETVAL = newSVpv( STRINGIFY(GIF_VERSION), 0 );
#else
  RETVAL = &PL_sv_undef;
#endif
}
OUTPUT:
  RETVAL