#pragma once
#include <panda/date/Date.h>

namespace panda { namespace date {

using panda::time::christ_days;

class Date;

class DateInt {
private:
    Date _from;
    Date _till;
    
    ptime_t hmsDiff () const;

public: 
    err_t set (const char* str, size_t len = 0);

    void set (ptime_t from, ptime_t till) {
        _from.set(from);
        _till.set(till);
    }

    void set (const Date* from, const Date* till) {
        _from.set(from);
        _till.set(till);
    }

    DateInt ()                                   : _from((ptime_t) 0), _till((ptime_t) 0)       {}
    DateInt (ptime_t from, ptime_t till)         : _from((ptime_t) from), _till((ptime_t) till) {}
    DateInt (const Date* from, const Date* till) : _from(from), _till(till)                     {}
    DateInt (const char* str, size_t len = 0)    : _from((ptime_t) 0), _till((ptime_t) 0)       { set(str, len); }

    err_t error () const { return _from.error() == E_OK ? _till.error() : _from.error(); }
    
    Date* from () { return &_from; }
    Date* till () { return &_till; }

    const char* to_string() const;

    ptime_t hms_diff () const {
        return (_till.hour() - _from.hour())*3600 + (_till.min() - _from.min())*60 + _till.sec() - _from.sec();
    }

    ptime_t duration () const { return error() ? 0 : (_till.epoch() - _from.epoch()); }
    ptime_t sec      () const { return duration(); }
    ptime_t imin     () const { return duration()/60; }
    double  min      () const { return (double) duration()/60; }
    ptime_t ihour    () const { return duration()/3600; }
    double  hour     () const { return (double) duration()/3600; }

    ptime_t iday     () const { return (ptime_t) day(); }
    double  day      () const { return christ_days(_till.year()) + _till.yday() - christ_days(_from.year()) - _from.yday() + (double) hms_diff() / 86400; }

    ptime_t imonth   () const { return (ptime_t) month(); }
    double  month    () const {
        return (_till.year() - _from.year())*12 + _till.month() - _from.month() +
               (double) (_till.day() - _from.day() + (double) hms_diff() / 86400) / _from.days_in_month();
    }

    ptime_t iyear () const { return (ptime_t) year(); }
    double  year  () const { return month() / 12; }

    DateRel* relative () const;
    
    DateInt* clone () const { return new DateInt(&_from, &_till); }
    
    DateInt* add (const DateRel* operand) {
        _from.add(operand);
        _till.add(operand);
        return this;
    }

    DateInt* subtract (const DateRel* operand) {
        _from.subtract(operand);
        _till.subtract(operand);
        return this;
    }

    DateInt* negative () {
        char tmp[sizeof(_from)];
        memcpy(tmp, &_from, sizeof(_from));
        memcpy(&_from, &_till, sizeof(_from));
        memcpy(&_till, tmp, sizeof(_from));
        return this;
    }

    DateInt* add_new      (const DateRel* operand) const { return clone()->add(operand); }
    DateInt* subtract_new (const DateRel* operand) const { return clone()->subtract(operand); }
    DateInt* negative_new ()                       const { return new DateInt(&_till, &_from); }

    int compare (const DateInt* operand) const {
        return epoch_cmp(duration(), operand->duration());
    }

    bool equals  (const DateInt* operand) const {
        return _from.equals(&operand->_from) && _till.equals(&operand->_till);
    }

    int includes (const Date* date) const {
        if (_from.gt(date)) return 1;
        if (_till.lt(date)) return -1;
        return 0;
    }

    ~DateInt () {}
};

}}
