#ifndef clipper_poly2av_h_
#define clipper_poly2av_h_

#include "myinit.h"

SV*
polygon2perl(pTHX_ const ClipperLib::Polygon& poly)
{
  AV* av = newAV();
  AV* innerav;
  const unsigned int len = poly.size();
  av_extend(av, len-1);
  for (unsigned int i = 0; i < len; i++) {
    innerav = newAV();
    av_store(av, i, newRV_noinc((SV*)innerav));
    av_fill(innerav, 1);
    // IVSIZE is from perl/lib/CORE/config.h, defined as sizeof(IV)
#if IVSIZE >= 8
    // if Perl integers are 64 bit, use newSViv()
    av_store(innerav, 0, newSViv(poly[i].X));
    av_store(innerav, 1, newSViv(poly[i].Y));
#else
    // otherwise we expect Clipper integers to fit in the
	// 53 bit mantissa of a Perl double
    av_store(innerav, 0, newSVnv(poly[i].X));
    av_store(innerav, 1, newSVnv(poly[i].Y));
#endif


  }
  return (SV*)newRV_noinc((SV*)av);
}


SV*
polygons2perl(pTHX_ const ClipperLib::Polygons& poly)
{
  AV* av = newAV();
  SV* innerav;
  const unsigned int len = poly.size();
  av_extend(av, len-1);
  for (unsigned int i = 0; i < len; i++) {
    innerav = polygon2perl(aTHX_ poly[i]);
    av_store(av, i, innerav);
  }
  return (SV*)newRV_noinc((SV*)av);
}


SV*
expolygon2perl(pTHX_ const ClipperLib::ExPolygon& poly)
{
  HV* hv = newHV();
  hv_stores( hv, "outer", (SV*)polygon2perl(aTHX_ poly.outer) );
  hv_stores( hv, "holes", (SV*)polygons2perl(aTHX_ poly.holes) );
  return (SV*)newRV_noinc((SV*)hv);
}


SV*
expolygons2perl(pTHX_ const ClipperLib::ExPolygons& polys)
{
  AV* av = newAV();
  SV* innerav;
  const unsigned int len = polys.size();
  av_extend(av, len-1);
  for (unsigned int i = 0; i < len; i++) {
    innerav = expolygon2perl(aTHX_ polys[i]);
    av_store(av, i, innerav);
  }
  return (SV*)newRV_noinc((SV*)av);
}



ClipperLib::Polygon*
perl2polygon(pTHX_ AV* theAv)
{
  const unsigned int len = av_len(theAv)+1;
  ClipperLib::Polygon* retval = new ClipperLib::Polygon(len);
  SV** elem;
  AV* innerav;
  for (unsigned int i = 0; i < len; i++) {
    elem = av_fetch(theAv, i, 0);
    if (!SvROK(*elem)
        || SvTYPE(SvRV(*elem)) != SVt_PVAV
        || av_len((AV*)SvRV(*elem)) < 1)
    {
      delete retval;
      return NULL;
    }
    innerav = (AV*)SvRV(*elem);
    ClipperLib::IntPoint& p = (*retval)[i];
    // IVSIZE is from perl/lib/CORE/config.h, defined as sizeof(IV)
#if IVSIZE >= 8
    // if Perl integers are 64 bit, use SvIV()
    // Clipper.pm then supports 64 bit ints.
    p.X = (ClipperLib::long64)SvIV(*av_fetch(innerav, 0, 0));
    p.Y = (ClipperLib::long64)SvIV(*av_fetch(innerav, 1, 0));
#else
    // otherwise coerce the Perl scalar to a double, with SvNV()
    // Perl doubles commonly allow 53 bits for the mantissa.
    // So in the common case, Clipper.pm supports 53 bit integers, stored in doubles on the Perl side.
    p.X = (ClipperLib::long64)SvNV(*av_fetch(innerav, 0, 0));
    p.Y = (ClipperLib::long64)SvNV(*av_fetch(innerav, 1, 0));
#endif
  }
  return retval;
}


ClipperLib::Polygons*
perl2polygons(pTHX_ AV* theAv)
{
  const unsigned int len = av_len(theAv)+1;
  ClipperLib::Polygons* retval = new ClipperLib::Polygons(len);
  SV** elem;
  AV* innerav;
  for (unsigned int i = 0; i < len; i++) {
    elem = av_fetch(theAv, i, 0);
    if (!SvROK(*elem)
        || SvTYPE(SvRV(*elem)) != SVt_PVAV
        || av_len((AV*)SvRV(*elem)) < 1)
    {
      delete retval;
      return NULL;
    }
    innerav = (AV*)SvRV(*elem);
    ClipperLib::Polygon* tmp = perl2polygon(aTHX_ innerav);
    if (tmp == NULL) {
      delete retval;
      return NULL;
    }
    (*retval)[i] = *tmp;
    delete tmp;
  }
  return retval;
}


#define AV_CHECK(outav, hv, key)                                                       \
    STMT_START {                                                                       \
      SV** buf = hv_fetchs(hv, key, 0);                                                \
      if (!buf || !*buf) {                                                             \
        croak("Missing ExPolygon hash key: '" key "' or not an array reference.");     \
      }                                                                                \
      SvGETMAGIC(*buf);                                                                \
      if (SvROK(*buf) && SvTYPE(SvRV(*buf)) == SVt_PVAV) {                             \
        outav = (AV*)SvRV(*buf);                                                       \
      }                                                                                \
      else {                                                                           \
        croak(key " is not an ARRAY reference");                                       \
      }                                                                                \
    } STMT_END

ClipperLib::ExPolygon*
perl2expolygon(pTHX_ HV* theHv)
{
  AV* outerav;
  AV* holesav;
  AV_CHECK(outerav, theHv, "outer");
  AV_CHECK(holesav, theHv, "holes");

  ClipperLib::ExPolygon* retval = new ClipperLib::ExPolygon();
  ClipperLib::Polygon* tmp = perl2polygon(aTHX_ outerav);
  if (!tmp) {
    delete retval;
    return NULL;
  }
  retval->outer = *tmp;

  ClipperLib::Polygons* tmps = perl2polygons(aTHX_ holesav);
  if (!tmps) {
    delete retval;
    return NULL;
  }
  retval->holes = *tmps;

  return retval;
}
#undef AV_CHECK


ClipperLib::ExPolygons*
perl2expolygons(pTHX_ AV* theAv)
{
  const unsigned int len = av_len(theAv)+1;
  std::vector<ClipperLib::ExPolygon> tmpEx; // Done because of croak

  SV** elem;
  HV* innerhv;
  for (unsigned int i = 0; i < len; i++) {
    elem = av_fetch(theAv, i, 0);
    if (!SvROK(*elem)
        || SvTYPE(SvRV(*elem)) != SVt_PVHV)
      return NULL;
    innerhv = (HV*)SvRV(*elem);
    ClipperLib::ExPolygon* tmp = perl2expolygon(aTHX_ innerhv);
    if (tmp == NULL)
      return NULL;
    tmpEx[i] = *tmp;
    delete tmp;
  }

  ClipperLib::ExPolygons* retval = new ClipperLib::ExPolygons(tmpEx);
  return retval;
}


#endif
