MODULE = FFI::Platypus PACKAGE = FFI::Platypus::Type

BOOT:
{
  HV *ft = get_hv("FFI::Platypus::TypeParser::ffi_type", GV_ADD);
  hv_stores(ft, "void",           newSViv(PTR2IV( &ffi_type_void           )));
  hv_stores(ft, "sint8",          newSViv(PTR2IV( &ffi_type_sint8          )));
  hv_stores(ft, "sint16",         newSViv(PTR2IV( &ffi_type_sint16         )));
  hv_stores(ft, "sint32",         newSViv(PTR2IV( &ffi_type_sint32         )));
  hv_stores(ft, "sint64",         newSViv(PTR2IV( &ffi_type_sint64         )));
  hv_stores(ft, "uint8",          newSViv(PTR2IV( &ffi_type_uint8          )));
  hv_stores(ft, "uint16",         newSViv(PTR2IV( &ffi_type_uint16         )));
  hv_stores(ft, "uint32",         newSViv(PTR2IV( &ffi_type_uint32         )));
  hv_stores(ft, "uint64",         newSViv(PTR2IV( &ffi_type_uint64         )));
  hv_stores(ft, "pointer",        newSViv(PTR2IV( &ffi_type_pointer        )));
  hv_stores(ft, "float",          newSViv(PTR2IV( &ffi_type_float          )));
  hv_stores(ft, "double",         newSViv(PTR2IV( &ffi_type_double         )));
#ifdef FFI_PL_PROBE_LONGDOUBLE
  hv_stores(ft, "longdouble",     newSViv(PTR2IV( &ffi_type_longdouble     )));
#endif
#ifdef FFI_PL_PROBE_COMPLEX
  hv_stores(ft, "complex_float",  newSViv(PTR2IV( &ffi_type_complex_float  )));
  hv_stores(ft, "complex_double", newSViv(PTR2IV( &ffi_type_complex_double )));
#endif
}

SV*
meta(self)
    ffi_pl_type *self
  PREINIT:
    HV *meta;
  CODE:
    meta = ffi_pl_get_type_meta(self);
    RETVAL = newRV_noinc((SV*)meta);
  OUTPUT:
    RETVAL

int
sizeof(self)
    ffi_pl_type *self
  CODE:
    RETVAL = ffi_pl_sizeof(self);
  OUTPUT:
    RETVAL

SV*
unitof(self)
    ffi_pl_type *self
  PREINIT:
    int type_code;
  CODE:
    type_code = self->type_code;

    /* ignore custom asoect */
    if((type_code & FFI_PL_SHAPE_MASK) == FFI_PL_SHAPE_CUSTOM_PERL)
    {
      type_code ^= FFI_PL_SHAPE_CUSTOM_PERL;
    }

    switch(type_code)
    {
      case FFI_PL_TYPE_SINT8 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_SINT8 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("sint8");
      case FFI_PL_TYPE_UINT8 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_UINT8 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("uint8");
      case FFI_PL_TYPE_SINT16 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_SINT16 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("sint16");
      case FFI_PL_TYPE_UINT16 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_UINT16 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("uint16");
      case FFI_PL_TYPE_SINT32 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_SINT32 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("sint32");
      case FFI_PL_TYPE_UINT32 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_UINT32 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("uint32");
      case FFI_PL_TYPE_SINT64 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_SINT64 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("sint64");
      case FFI_PL_TYPE_UINT64 | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_UINT64 | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("uint64");
      case FFI_PL_TYPE_FLOAT | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_FLOAT | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("float");
      case FFI_PL_TYPE_DOUBLE | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_DOUBLE | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("double");
#ifdef FFI_PL_PROBE_LONGDOUBLE
      case FFI_PL_TYPE_LONG_DOUBLE | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_LONG_DOUBLE | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("longdouble");
#endif
#ifdef FFI_PL_PROBE_COMPLEX
      case FFI_PL_TYPE_COMPLEX_FLOAT | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_COMPLEX_FLOAT | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("complex_float");

      case FFI_PL_TYPE_COMPLEX_DOUBLE | FFI_PL_SHAPE_POINTER:
      case FFI_PL_TYPE_COMPLEX_DOUBLE | FFI_PL_SHAPE_ARRAY:
        XSRETURN_PV("complex_double");
#endif
      default:
        XSRETURN_UNDEF;
    }

const char *
kindof(self)
    ffi_pl_type *self
  PREINIT:
    int type_code;
  CODE:
    type_code = self->type_code;

    /* ignore custom asoect */
    if((type_code & FFI_PL_SHAPE_MASK) == FFI_PL_SHAPE_CUSTOM_PERL)
    {
      type_code ^= FFI_PL_SHAPE_CUSTOM_PERL;
    }

    switch(type_code)
    {
      case FFI_PL_TYPE_VOID :
        RETVAL = "void";
        break;

      case FFI_PL_TYPE_SINT8:
      case FFI_PL_TYPE_UINT8:
      case FFI_PL_TYPE_SINT16:
      case FFI_PL_TYPE_UINT16:
      case FFI_PL_TYPE_SINT32:
      case FFI_PL_TYPE_UINT32:
      case FFI_PL_TYPE_SINT64:
      case FFI_PL_TYPE_UINT64:
      case FFI_PL_TYPE_FLOAT:
      case FFI_PL_TYPE_DOUBLE:
      case FFI_PL_TYPE_LONG_DOUBLE:
      case FFI_PL_TYPE_COMPLEX_FLOAT:
      case FFI_PL_TYPE_COMPLEX_DOUBLE:
      case FFI_PL_TYPE_OPAQUE:
        RETVAL = "scalar";
        break;

      case FFI_PL_TYPE_STRING:
        RETVAL = "string";
        break;

      case FFI_PL_TYPE_CLOSURE:
        RETVAL = "closure";
        break;

      case FFI_PL_TYPE_RECORD:
        RETVAL = "record";
        break;

      case FFI_PL_TYPE_RECORD_VALUE:
        RETVAL = "record-value";
        break;

      default:
        switch(type_code & FFI_PL_SHAPE_MASK)
        {
          case FFI_PL_SHAPE_POINTER:
            RETVAL = "pointer";
            break;
          case FFI_PL_SHAPE_ARRAY:
            RETVAL = "array";
            break;
          case FFI_PL_SHAPE_OBJECT:
            RETVAL = "object";
            break;
          default:
            croak("internal error computing type kind (%x)", type_code);
        }
    }
  OUTPUT:
    RETVAL

int
countof(self)
    ffi_pl_type *self
  CODE:
    switch(self->type_code & FFI_PL_SHAPE_MASK)
    {
      case FFI_PL_SHAPE_SCALAR:
      case FFI_PL_SHAPE_POINTER:
      case FFI_PL_SHAPE_CUSTOM_PERL:
      case FFI_PL_SHAPE_OBJECT:
        switch(self->type_code)
        {
          case FFI_PL_TYPE_VOID:
            RETVAL = 0;
            break;
          default:
            RETVAL = 1;
            break;
        }
        break;

      case FFI_PL_SHAPE_ARRAY:
        RETVAL = self->extra[0].array.element_count;
        break;

      default:
        croak("internal error computing type kind (%x)", self->type_code);
    }
  OUTPUT:
    RETVAL

int
type_code(self)
    ffi_pl_type *self
  CODE:
    RETVAL = self->type_code;
  OUTPUT:
    RETVAL

int
is_record(self)
    ffi_pl_type *self
  CODE:
    /* may not need this method anymore */
    RETVAL = self->type_code == FFI_PL_TYPE_RECORD
    ||       self->type_code == (FFI_PL_TYPE_RECORD | FFI_PL_SHAPE_CUSTOM_PERL);
  OUTPUT:
    RETVAL

int
is_record_value(self)
    ffi_pl_type *self
  CODE:
    RETVAL = self->type_code == FFI_PL_TYPE_RECORD_VALUE
    ||       self->type_code == (FFI_PL_TYPE_RECORD_VALUE | FFI_PL_SHAPE_CUSTOM_PERL);
  OUTPUT:
    RETVAL

int
is_customizable(self)
    ffi_pl_type *self
  PREINIT:
    int shape;
    int base;
  CODE:
    shape = self->type_code & FFI_PL_SHAPE_MASK;
    base  = self->type_code & FFI_PL_BASE_MASK;
    RETVAL = shape == FFI_PL_SHAPE_SCALAR
          && (   base == FFI_PL_BASE_SINT
              || base == FFI_PL_BASE_UINT
              || base == FFI_PL_BASE_FLOAT
              || base == FFI_PL_BASE_OPAQUE
              || base == FFI_PL_BASE_RECORD
              || base == (FFI_PL_BASE_RECORD | FFI_PL_BASE_OPAQUE)
             );
  OUTPUT:
    RETVAL

int
is_object_ok(self)
    ffi_pl_type *self
  PREINIT:
    int shape;
    int base;
  CODE:
    shape = self->type_code & FFI_PL_SHAPE_MASK;
    base  = self->type_code & FFI_PL_BASE_MASK;
    RETVAL = shape == FFI_PL_SHAPE_SCALAR
           && (   base == FFI_PL_BASE_SINT
               || base == FFI_PL_BASE_UINT
               || base == FFI_PL_BASE_OPAQUE
              );
  OUTPUT:
    RETVAL

int
is_ro(self)
    ffi_pl_type *self
  CODE:
    RETVAL = self->type_code == FFI_PL_TYPE_STRING     &&
             self->sub_type  == FFI_PL_TYPE_STRING_RO;
  OUTPUT:
    RETVAL

void
DESTROY(self)
    ffi_pl_type *self
  CODE:
    if(self->type_code == FFI_PL_TYPE_CLOSURE)
    {
      if(!PL_dirty)
        Safefree(self->extra[0].closure.ffi_cif.arg_types);
    }
    else if(self->type_code == FFI_PL_TYPE_RECORD
    ||      self->type_code == FFI_PL_TYPE_RECORD_VALUE)
    {
      if(self->extra[0].record.class != NULL)
        free(self->extra[0].record.class);
    }
    else
    {
      switch(self->type_code & FFI_PL_SHAPE_MASK)
      {
        case FFI_PL_SHAPE_CUSTOM_PERL:
          {
            ffi_pl_type_extra_custom_perl *custom;

            custom = &self->extra[0].custom_perl;

            if(custom->perl_to_native != NULL)
              SvREFCNT_dec(custom->perl_to_native);
            if(custom->perl_to_native_post != NULL)
              SvREFCNT_dec(custom->perl_to_native_post);
            if(custom->native_to_perl != NULL)
              SvREFCNT_dec(custom->native_to_perl);
            if(self->extra[0].record.class != NULL)
              free(self->extra[0].record.class);
          }
          break;
        case FFI_PL_SHAPE_OBJECT:
          {
            if(self->extra[0].object.class != NULL)
              free(self->extra[0].object.class);
          }
          break;
        default:
          break;
      }
    }
    if(!PL_dirty)
      Safefree(self);