#undef SV2C_DATE_FUNC
#undef SV2C_DATE_ACTION
#undef SV2C_DATE_DEFVALS
#undef SV2C_DATEREL_FUNC
#undef SV2C_DATEREL_ACTION
#undef SV2C_DATEINT_FUNC
#undef SV2C_DATEINT_ACTION

#ifdef SV2C_NEW
#  define SV2C_DATE_FUNC      date_new
#  define SV2C_DATE_ACTION    operand = new Date
#  define SV2C_DATE_DEFVALS   {2000, 1, 1, 0, 0, 0, -1}
#  define SV2C_DATEREL_FUNC   daterel_new
#  define SV2C_DATEREL_ACTION operand = new DateRel
#  define SV2C_DATEINT_FUNC   dateint_new
#  define SV2C_DATEINT_ACTION operand = new DateInt
#elif defined SV2C_SET
#  define SV2C_DATE_FUNC      date_set
#  define SV2C_DATE_ACTION    operand->set
#  define SV2C_DATE_DEFVALS   {2000, 1, 1, 0, 0, 0, -1}
#  define SV2C_DATEREL_FUNC   daterel_set
#  define SV2C_DATEREL_ACTION operand->set
#  define SV2C_DATEINT_FUNC   dateint_set
#  define SV2C_DATEINT_ACTION operand->set
#elif defined SV2C_CLONE
#  define SV2C_DATE_FUNC    date_clone
#  define SV2C_DATE_ACTION  operand = operand->clone
#  define SV2C_DATE_DEFVALS {-1, -1, -1, -1, -1, -1, -1}
#else
#  error "should not be here"
#endif

#ifndef SV2C_TYPE_CROAK
#  define SV2C_TYPE_CROAK croak("Panda::Date: cannot create/set/clone object - argument of unknown type passed")
#endif

namespace xs { namespace date {

Date* SV2C_DATE_FUNC (pTHX_ SV* arg, const tz* zone, Date* operand) {
    ptime_t epoch = 0;
    
    if (SvOK(arg)) {
        if (SvROK(arg)) {
            if (sv_isobject(arg)) {
#ifndef SV2C_CLONE
                if (sv_isa(arg, DATE_CLASS)) {
                    SV2C_DATE_ACTION((Date *) SvIV(SvRV(arg)), zone);
                    return operand;
                }
                else SV2C_TYPE_CROAK;
#endif
            }
            else {
                SV* rarg = SvRV(arg);
                ptime_t vals[] = SV2C_DATE_DEFVALS;
                SV** ref;
                if (SvTYPE(rarg) == SVt_PVHV) {
                    HV* hash = (HV*) rarg;
                    
                    ref = hv_fetch(hash, "year", 4, 0);
                    if (ref != NULL) vals[0] = SvMIV(*ref);
                    ref = hv_fetch(hash, "month", 5, 0);
                    if (ref != NULL) vals[1] = SvMIV(*ref);
                    ref = hv_fetch(hash, "day", 3, 0);
                    if (ref != NULL) vals[2] = SvMIV(*ref);
                    ref = hv_fetch(hash, "hour", 4, 0);
                    if (ref != NULL) vals[3] = SvMIV(*ref);
                    ref = hv_fetch(hash, "min", 3, 0);
                    if (ref != NULL) vals[4] = SvMIV(*ref);
                    ref = hv_fetch(hash, "sec", 3, 0);
                    if (ref != NULL) vals[5] = SvMIV(*ref);
                    ref = hv_fetch(hash, "isdst", 5, 0);
                    if (ref != NULL) vals[6] = SvMIV(*ref);
                    
                    if (zone == NULL) {
                        ref = hv_fetch(hash, "tz", 2, 0);
                        if (ref != NULL) zone = tzget_required(aTHX_ *ref);
                    }
                }
                else if (SvTYPE(rarg) == SVt_PVAV) {
                    AV* array = (AV*) rarg;
                    I32 len = av_len(array);
                    for (int i = 0; i <= len; i++) {
                        ref = av_fetch(array, i, 0);
                        if (ref != NULL && SvOK(*ref)) vals[i] = SvMIV(*ref);
                    }
                } else {
                    SV2C_TYPE_CROAK;
                }

                SV2C_DATE_ACTION(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], zone);
                return operand;                
            }
        }
#ifndef SV2C_CLONE
        else if (looks_like_number(arg)) {
            epoch = SvMIV(arg);
        }
        else {
            STRLEN len;
            const char* str = SvPV(arg, len);
            SV2C_DATE_ACTION(str, len, zone);
            return operand;
        }
#endif
    }

#ifdef SV2C_CLONE
    SV2C_TYPE_CROAK;
#else
    SV2C_DATE_ACTION(epoch, zone);
    return operand;
#endif
}

#ifdef SV2C_DATEREL_FUNC

DateRel* SV2C_DATEREL_FUNC (pTHX_ SV* arg, DateRel* operand) {
    if (!SvOK(arg)) {
        SV2C_DATEREL_ACTION(0,0,0,0,0,0);
        return operand;
    }
    
    ptime_t vals[] = {0, 0, 0, 0, 0, 0};

    if (SvROK(arg)) {
        if (sv_isobject(arg) && sv_isa(arg, DATEREL_CLASS)) {
            SV2C_DATEREL_ACTION((DateRel *) SvIV(SvRV(arg)));
            return operand;
        }
        else {
            SV* rarg = SvRV(arg);
            SV** ref;
            if (SvTYPE(rarg) == SVt_PVHV) {
                HV* hash = (HV*) rarg;
                ref = hv_fetch(hash, "year", 4, 0);
                if (ref != NULL) vals[0] = SvMIV(*ref);
                ref = hv_fetch(hash, "month", 5, 0);
                if (ref != NULL) vals[1] = SvMIV(*ref);
                ref = hv_fetch(hash, "day", 3, 0);
                if (ref != NULL) vals[2] = SvMIV(*ref);
                ref = hv_fetch(hash, "hour", 4, 0);
                if (ref != NULL) vals[3] = SvMIV(*ref);
                ref = hv_fetch(hash, "min", 3, 0);
                if (ref != NULL) vals[4] = SvMIV(*ref);
                ref = hv_fetch(hash, "sec", 3, 0);
                if (ref != NULL) vals[5] = SvMIV(*ref);
            }
            else if (SvTYPE(rarg) == SVt_PVAV) {
                AV* array = (AV*) rarg;
                I32 len = av_len(array);
                for (int i = 0; i <= len; i++) {
                    ref = av_fetch(array, i, 0);
                    if (ref != NULL) vals[i] = SvMIV(*ref);
                }
            }
            else {
                SV2C_TYPE_CROAK;
            }
            
            SV2C_DATEREL_ACTION(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
            return operand;                 
        }
    }
    else if (looks_like_number(arg)) {
        SV2C_DATEREL_ACTION(0, 0, 0, 0, 0, SvMIV(arg));
        return operand;
    }
    else {
        STRLEN len;
        const char* str = SvPV(arg, len);
        SV2C_DATEREL_ACTION(str, len);
        return operand;
    }
}

DateRel* SV2C_DATEREL_FUNC (pTHX_ SV* fromSV, SV* tillSV, DateRel* operand) {
    Date from((ptime_t) 0);
    Date till((ptime_t) 0);
    date_set(aTHX_ fromSV, NULL, &from);
    date_set(aTHX_ tillSV, NULL, &till);
    SV2C_DATEREL_ACTION(from.date(), till.date());
    return operand;
}

#endif

#ifdef SV2C_DATEINT_FUNC

DateInt* SV2C_DATEINT_FUNC (pTHX_ SV* arg, DateInt* operand) {
    if (SvOK(arg) && SvROK(arg)) {
        SV* argval = SvRV(arg);
        if (SvTYPE(argval) == SVt_PVAV) {
            AV* arr = (AV*) argval;
            SV** elemref1 = av_fetch(arr, 0, 0);
            SV** elemref2 = av_fetch(arr, 1, 0);
            if (elemref1 != NULL && elemref2 != NULL) return SV2C_DATEINT_FUNC(aTHX_ *elemref1, *elemref2, operand);
        }
    }
    else if (SvPOK(arg)) {
        STRLEN len;
        const char* str = SvPV(arg, len);
        SV2C_DATEINT_ACTION(str, len);
        return operand;
    }
    
    SV2C_TYPE_CROAK;
}

DateInt* SV2C_DATEINT_FUNC (pTHX_ SV* fromSV, SV* tillSV, DateInt* operand) {
    Date from((ptime_t) 0);
    Date till((ptime_t) 0);
    date_set(aTHX_ fromSV, NULL, &from);
    date_set(aTHX_ tillSV, NULL, &till);
    SV2C_DATEINT_ACTION(&from, &till);
    return operand;
}

#endif

}}
