MODE: INLINE

#include "private.h"

static inline DateRel xs_daterel_ctor (SV* arg, SV* till) {
    return till ? DateRel(sv2date(arg), sv2date(till)) : sv2daterel(arg);
}

static inline DateRel xs_daterel_ymd (SV** args, I32 items) {
    ptime_t vals[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    list2vals(args, items, vals);
    return DateRel(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
}

MODULE = Date                PACKAGE = Date
PROTOTYPES: DISABLE

BOOT {
    Stash stash("Date::Rel", GV_ADD);
    
    xs::exp::create_constants(stash, {
        {"FORMAT_SIMPLE",   (int)DateRel::Format::simple},
        {"FORMAT_ISO8601D", (int)DateRel::Format::iso8601d},
        {"FORMAT_ISO8601I", (int)DateRel::Format::iso8601i},
        
        {"INPUT_FORMAT_ALL",      DateRel::InputFormat::all},
        {"INPUT_FORMAT_SIMPLE",   DateRel::InputFormat::simple},
        {"INPUT_FORMAT_ISO8601",  DateRel::InputFormat::iso8601},
        {"INPUT_FORMAT_ISO8601D", DateRel::InputFormat::iso8601d},
        {"INPUT_FORMAT_ISO8601I", DateRel::InputFormat::iso8601i},
    });
}

#///////////////////////////// STATIC FUNCTIONS ///////////////////////////////////

DateRel* rdate (SV* from = {}, SV* till = {}) {
    RETVAL = new DateRel(xs_daterel_ctor(from, till));
}

DateRel* rdate_ymd (...) {
    RETVAL = new DateRel(xs_daterel_ymd(&ST(0), items));
}

const DateRel* rdate_const (SV* from = {}, SV* till = {}) {
    RETVAL = new DateRel(xs_daterel_ctor(from, till));
}

const DateRel* rdate_ymd_const (...) {
    RETVAL = new DateRel(xs_daterel_ymd(&ST(0), items));
}

#///////////////////////////// OBJECT METHODS ///////////////////////////////////
MODULE = Date                PACKAGE = Date::Rel
PROTOTYPES: DISABLE

DateRel* new (SV*, SV* from = {}, SV* till = {}) {
    RETVAL = new DateRel(xs_daterel_ctor(from, till));
}

DateRel* new_ymd (...) {
    RETVAL = new DateRel(xs_daterel_ymd(&ST(1), items - 1));
}

void DateRel::set (SV* from, SV* till = {}) {
    *THIS = xs_daterel_ctor(from, till);
}

void DateRel::set_ymd (...) {
    *THIS = xs_daterel_ymd(&ST(1), items - 1);
}

std::error_code DateRel::error ()

ptime_t DateRel::sec (SV* newval = NULL) {
    if (newval) {
        THIS->sec(xs::in<ptime_t>(newval));
    }
    RETVAL = THIS->sec();
}

ptime_t DateRel::min (SV* newval = NULL) {
    if (newval) THIS->min(xs::in<ptime_t>(newval));
    RETVAL = THIS->min();
}

ptime_t DateRel::hour (SV* newval = NULL) {
    if (newval) {
        THIS->hour(xs::in<ptime_t>(newval));
    }
    RETVAL = THIS->hour();
}

ptime_t DateRel::day (SV* newval = NULL) {
    if (newval) {
        THIS->day(xs::in<ptime_t>(newval));
    }
    RETVAL = THIS->day();
}

ptime_t DateRel::month (SV* newval = NULL) {
    if (newval) {
        THIS->month(xs::in<ptime_t>(newval));
    }
    RETVAL = THIS->month();
}

ptime_t DateRel::year (SV* newval = NULL) {
    if (newval) {
        THIS->year(xs::in<ptime_t>(newval));
    }
    RETVAL = THIS->year();
}

Date* DateRel::from (SV* fromSV = NULL) {
    if (fromSV) {
        THIS->from(sv2date(fromSV));
        XSRETURN_UNDEF;
    }
    if (!THIS->from()) XSRETURN_UNDEF;
    RETVAL = new Date(*(THIS->from()));
}

Date* DateRel::till () : const {
    auto till = THIS->till();
    if (!till) XSRETURN_UNDEF;
    RETVAL = new Date(*till);
}

ptime_t DateRel::to_secs () : ALIAS(duration=1) const {
    PERL_UNUSED_VAR(ix);
    RETVAL = THIS->to_secs();
}

double DateRel::to_mins () : const

double DateRel::to_hours () : const

double DateRel::to_days () : const

double DateRel::to_months () : const

double DateRel::to_years () : const

string DateRel::to_string (int format = (int)DateRel::Format::simple) : const {
    RETVAL = THIS->to_string((DateRel::Format)format);
}

string DateRel::_op_str (...) : const {
    RETVAL = THIS->to_string();
}

bool DateRel::to_bool (...) : const {
    RETVAL = THIS->duration();
}

ptime_t DateRel::to_number (...) : const {
    RETVAL = THIS->duration();
}

Sv DateRel::sum (Sv arg, ...) : const {
    if (arg.is_object_ref()) {
        Stash s = Object(arg).stash();
        if (s.name() == "Date") RETVAL = xs::out(new Date(*xs::in<Date*>(arg) + *THIS));
        else                    RETVAL = xs::out(new DateRel(*THIS + *xs::in<const DateRel*>(arg)), Object(ST(0)).stash());
    }
    else RETVAL = xs::out(new DateRel(*THIS + sv2daterel(arg)), Object(ST(0)).stash());
}

SV* DateRel::add (Sv arg, ...) {
    *THIS += sv2daterel(arg);
    XSRETURN(1);
}

DateRel* DateRel::difference (Sv arg, bool reverse = false) : const {
    auto op = sv2daterel(arg);
    RETVAL = new DateRel(reverse ? (op - *THIS) : (*THIS - op));
    PROTO = Object(ST(0)).stash();
}

SV* DateRel::subtract (Sv arg, ...) {
    *THIS -= sv2daterel(arg);
    XSRETURN(1);
}

DateRel* DateRel::product (SV* arg, ...) : const {
    RETVAL = new DateRel(*THIS * SvNV(arg));
    PROTO = Object(ST(0)).stash();
}

SV* DateRel::multiply (SV* arg, ...) {
    *THIS *= SvNV(arg);
    XSRETURN(1);
}

DateRel* DateRel::quotient (SV* arg, bool reverse = false) : const {
    if (reverse) throw "Date: illegal divison $num/$reldate";
    RETVAL = new DateRel(*THIS / SvNV(arg));
    PROTO = Object(ST(0)).stash();
}

SV* DateRel::divide (SV* arg, bool reverse = false) {
    if (reverse) throw "Date: illegal divison $num/$reldate";
    *THIS /= SvNV(arg);
    XSRETURN(1);
}

DateRel* DateRel::negated (...) : const {
    RETVAL = new DateRel(THIS->negated());
    PROTO = Object(ST(0)).stash();
}

SV* DateRel::negate () {
    THIS->negate();
    XSRETURN(1);
}

int DateRel::compare (Sv arg, bool reverse) : const {
    RETVAL = THIS->compare(sv2daterel(arg));
    if (reverse) RETVAL = -RETVAL;
    if      (RETVAL < 0) RETVAL = -1;
    else if (RETVAL > 0) RETVAL = 1;
}

bool DateRel::is_same (Sv arg, ...) : const {
    RETVAL = THIS->is_same(sv2daterel(arg));
}

int DateRel::includes (Sv arg) : const {
    RETVAL = THIS->includes(sv2date(arg));
}

DateRel* DateRel::clone () {
    RETVAL = new DateRel(*THIS);
    PROTO = Object(ST(0)).stash();
}