#include <panda/lib.h>
#include <panda/date/DateRel.h>

#define RELSTR_START(maxlen)\
    static char ret[maxlen+1];\
    char* ptr = ret;\
    char* buf;
    
#define RELSTR_VAL(val,units)\
    if (ptr != ret) *(ptr++) = ' ';\
    buf = itoa(val);\
    while (*buf) *(ptr++) = *(buf++);\
    *(ptr++) = units;

#define RELSTR_END\
    *(ptr++) = 0;\
    return ret;
    
namespace panda { namespace date {

using panda::lib::itoa;
    
void DateRel::set (const dt* from, const dt* till) {
    _PDR_CHKCONST;
    _sec = 0, _min = 0, _hour = 0, _day = 0, _month = 0, _year = 0;
    bool reverse = false;
    if (date_cmp(from, till) > 0) {
        reverse = true;
        std::swap(from, till);
    }
    
    _sec = till->sec - from->sec;
    if (_sec < 0) { _sec += 60; _min--; }
    
    _min += till->min - from->min;
    if (_min < 0) { _min += 60; _hour--; }
    
    _hour += till->hour - from->hour;
    if (_hour < 0) { _hour += 24; _day--; }
    
    _day += till->mday - from->mday;
    if (_day < 0) {
        int tmpy = till->year;
        int tmpm = till->mon-1;
        if (tmpm < 0) { tmpm += 12; tmpy--; }
        int days = days_in_month(tmpy, tmpm);
        _day += days;
        _month--;
    }
    
    _month += till->mon - from->mon;
    if (_month < 0) { _month += 12; _year--; }
    
    _year += till->year - from->year;
    
    if (reverse) negative();
}

const char* DateRel::to_string () const {
    RELSTR_START(65);
    if (_year  != 0) { RELSTR_VAL(_year, 'Y') }
    if (_month != 0) { RELSTR_VAL(_month, 'M') }
    if (_day   != 0) { RELSTR_VAL(_day, 'D') }
    if (_hour  != 0) { RELSTR_VAL(_hour, 'h') }
    if (_min   != 0) { RELSTR_VAL(_min, 'm') }
    if (_sec   != 0) { RELSTR_VAL(_sec, 's') }
    RELSTR_END;
}

DateRel* DateRel::multiply (double koef) {
    _PDR_CHKCONST; 
    if (fabs(koef) < 1 && koef != 0) return divide(1/koef);
    _sec   *= koef;
    _min   *= koef;
    _hour  *= koef;
    _day   *= koef;
    _month *= koef;
    _year  *= koef;
    return this;
}

DateRel* DateRel::divide (double koef) {
    _PDR_CHKCONST;
    if (fabs(koef) <= 1) return multiply(1/koef);
    double td;
    int64_t tmp;
    
    tmp = _year;
    _year /= koef;
    td = (tmp - _year*koef)*12;
    tmp = td;
    _month += tmp;
    td = (td - tmp)*((double)2629744/86400);
    tmp = td;
    _day += tmp;
    td = (td - tmp)*24;
    tmp = td;
    _hour += tmp;
    td = (td - tmp)*60;
    tmp = td;
    _min += tmp;
    td = (td - tmp)*60;
    _sec += td;

    tmp = _month;
    _month /= koef;
    td = (tmp - _month*koef)*((double)2629744/86400);
    tmp = td;
    _day += tmp;
    td = (td - tmp)*24;
    tmp = td;
    _hour += tmp;
    td = (td - tmp)*60;
    tmp = td;
    _min += tmp;
    td = (td - tmp)*60;
    _sec += td;
    
    tmp = _day;
    _day /= koef;
    td = (tmp - _day*koef)*24;
    tmp = td;
    _hour += tmp;
    td = (td - tmp)*60;
    tmp = td;
    _min += tmp;
    td = (td - tmp)*60;
    _sec += td;
    
    tmp = _hour;
    _hour /= koef;
    td = (tmp - _hour*koef)*60;
    tmp = td;
    _min += tmp;
    td = (td - tmp)*60;
    _sec += td;
    
    tmp = _min;
    _min /= koef;
    _sec += (tmp - _min*koef)*60;
    
    _sec /= koef;
    
    return this;
}

DateRel* DateRel::add (const DateRel* operand) {
    _PDR_CHKCONST;
    _sec   += operand->_sec;
    _min   += operand->_min;
    _hour  += operand->_hour;
    _day   += operand->_day;
    _month += operand->_month;
    _year  += operand->_year;
    return this;
}

DateRel* DateRel::subtract (const DateRel* operand) {
    _PDR_CHKCONST;
    _sec   -= operand->_sec;
    _min   -= operand->_min;
    _hour  -= operand->_hour;
    _day   -= operand->_day;
    _month -= operand->_month;
    _year  -= operand->_year;
    return this;
}

DateRel* DateRel::negative () {
    _PDR_CHKCONST;
    _sec   = -_sec;
    _min   = -_min;
    _hour  = -_hour;
    _day   = -_day;
    _month = -_month;
    _year  = -_year;
    return this;
}

}}
