#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "ffi_platypus.h"
#include "ffi_platypus_guts.h"

XS(ffi_pl_record_accessor_string_ro)
{
  ffi_pl_record_member *member;
  SV *self;
  char *ptr1;
  char **ptr2;

  dVAR; dXSARGS;

  if(items == 0)
    croak("This is a method, you must provide at least the object");

  member = (ffi_pl_record_member*) CvXSUBANY(cv).any_ptr;

  self = ST(0);
  if(SvROK(self))
    self = SvRV(self);

  if(!SvOK(self))
    croak("Null record error");

  ptr1 = (char*) SvPV_nolen(self);
  ptr2 = (char**) &ptr1[member->offset];

  if(items > 1)
  {
    croak("member is read only");
  }

  if(GIMME_V == G_VOID)
    XSRETURN_EMPTY;

  if(*ptr2 != NULL)
    XSRETURN_PV(*ptr2);
  else
    XSRETURN_EMPTY;
}

XS(ffi_pl_record_accessor_string_rw)
{
  ffi_pl_record_member *member;
  SV *self;
  SV *arg;
  char *ptr1;
  char **ptr2;
  char *arg_ptr;
  STRLEN len;

  dVAR; dXSARGS;

  if(items == 0)
    croak("This is a method, you must provide at least the object");

  member = (ffi_pl_record_member*) CvXSUBANY(cv).any_ptr;

  self = ST(0);
  if(SvROK(self))
    self = SvRV(self);

  if(!SvOK(self))
    croak("Null record error");

  ptr1 = (char*) SvPV_nolen(self);
  ptr2 = (char**) &ptr1[member->offset];

  if(items > 1)
  {
    if(SvREADONLY(self))
    {
      croak("record is read-only");
    }
    else
    {
      arg = ST(1);
      if(SvOK(arg))
      {
        arg_ptr = SvPV(arg, len);
        *ptr2 = realloc(*ptr2, len+1);
        (*ptr2)[len] = 0;
        memcpy(*ptr2, arg_ptr, len);
      }
      else if(*ptr2 != NULL)
      {
        free(*ptr2);
        *ptr2 = NULL;
      }
    }
  }

  if(GIMME_V == G_VOID)
    XSRETURN_EMPTY;

  if(*ptr2 != NULL)
    XSRETURN_PV(*ptr2);
  else
    XSRETURN_EMPTY;
}

XS(ffi_pl_record_accessor_string_fixed)
{
  ffi_pl_record_member *member;
  SV *self;
  SV *arg;
  SV *value;
  char *ptr1;
  char *ptr2;
  char *arg_ptr;
  STRLEN len;

  dVAR; dXSARGS;

  if(items == 0)
    croak("This is a method, you must provide at least the object");

  member = (ffi_pl_record_member*) CvXSUBANY(cv).any_ptr;

  self = ST(0);
  if(SvROK(self))
    self = SvRV(self);

  if(!SvOK(self))
    croak("Null record error");

  ptr1 = (char*) SvPV_nolen(self);
  ptr2 = (char*) &ptr1[member->offset];

  if(items > 1)
  {
    if(SvREADONLY(self))
    {
      croak("record is read-only");
    }
    else
    {
      arg = ST(1);
      if(SvOK(arg))
      {
        arg_ptr = SvPV(ST(1), len);
        if(len > member->count)
          len = member->count;
        memcpy(ptr2, arg_ptr, len);
      }
      else
      {
        croak("Cannot assign undef to a fixed string field");
      }
    }
  }

  if(GIMME_V == G_VOID)
    XSRETURN_EMPTY;

  value = sv_newmortal();
  sv_setpvn(value, ptr2, member->count);
  ST(0) = value;
  XSRETURN(1);
}