/* A kind of util, for speeding up operations on Year-Month-Day related
   to Polish law:
   - kodeks cywilny art. 111-116,
   - for comments, look at http://www.rp.pl/artykul/56643,112899_Jak_liczyc_terminy_w_prawie_pracy.html
   The reason why not to use Date class, is only the specific
   set of operations needed by non-linear YMD-to-time
   relationship, like:
   - simple iterators (like month to next month),
   - delta calculations mode (like counting
   how many years/months/days are between to YMDs), which
   is consistent according to financial operations,
   - date validators,
*/

function DateYMD(y, m, d)
{
    this.y = y;
    this.m = m;
    this.d = d;
}

function createDateYMDfromNow()
{
    var date = new Date();
    return new DateYMD(date.getFullYear(), date.getMonth(), date.getDate());
}

function createDateYMDfromYMD(ymd)
{
    return new DateYMD(ymd.y, ymd.m, ymd.d);
}

function createDateYMDV(y, m, d, v)
{
    var obj = new DateYMD(y, m, d);
    obj.v   = v;
    return obj;
}

function DateYMD_getString()
{
    return this.y + '.' + (this.m+1) + '.' + (this.d+1);
}

function DateYMD_isValid()
{
    if ((typeof this.y != "number") || (typeof this.m != "number") || (typeof this.d != "number")
        || (this.y < 0) || (this.m < 0) || (this.d < 0)
        || (this.d >= DAYS_inMonth(this.y, this.m)
        || (this.y > 3000) || (this.m > 11))
        )
    {
        return 0;
    }

    return 1;
}

function DateYMD_isBefore(cmpObj)
{
    if (    (this.y <  cmpObj.y)
        || ((this.y == cmpObj.y) && (this.m <  cmpObj.m))
        || ((this.y == cmpObj.y) && (this.m == cmpObj.m) && (this.d < cmpObj.d))
        )
    {
        return 1;
    }

    return 0;
}

function DateYMD_isAfter(cmpObj)
{
    if (    (this.y >  cmpObj.y)
        || ((this.y == cmpObj.y) && (this.m >  cmpObj.m))
        || ((this.y == cmpObj.y) && (this.m == cmpObj.m) && (this.d > cmpObj.d))
        )
    {
        return 1;
    }

    return 0;
}

function DateYMD_setFromYMD(fromObj)
{
    this.y = fromObj.y;
    this.m = fromObj.m;
    this.d = fromObj.d;
}

function DateYMD_nextMonth()
{
    this.m++;
    if (this.m == 12)
    {
        this.m = 0;
        this.y++;
    }
}

function DateYMD_nextDay()
{
    this.d++;
    if (this.d == DAYS_inMonth(this.y, this.m))
    {
        this.d = 0;
        this.m++;
        if (this.m == 12)
        {
            this.m = 0;
            this.y++;
        }
    }
}

function DateYMD_diffYMDToYMD(toYMD)
{
    var resultObj = new DateYMD(0, 0, 0);

    if (this.isBefore(toYMD))
    {
        var iter      = new createDateYMDfromYMD(this);

        /* go year by year */
        while (!iter.isAfter(toYMD))
        {
            iter.y++;
            resultObj.y++;
        }
        iter.y--;
        resultObj.y--;

        /* go month by month */
        var backup = new createDateYMDfromYMD(iter);
        while (!iter.isAfter(toYMD))
        {
            backup.setFromYMD(iter);
            iter.m++;
            if (iter.m == 12)
            {
                iter.m = 0;
                iter.y++;
            }
            resultObj.m++;
        }
        iter.setFromYMD(backup);
        resultObj.m--;

        /* go day by day */
        while (!iter.isAfter(toYMD))
        {
            backup.setFromYMD(iter);
            iter.d++;
            if (iter.d == DAYS_inMonth(this.y, this.m))
            {
                iter.d = 0;
                iter.m++;
                if (iter.m == 12)
                {
                    iter.m = 0;
                    iter.y++;
                }
            }
            resultObj.d++;
        }
        iter.setFromYMD(backup);

        /* we prefer seeing 5m 0d, than 4m 30d, if the 30d is last day in the 4th month
           the same way, we prefer to see 1y 0m 0d, rather than 0y 11m 30d or - worse 0y 12m 0d */
        if (resultObj.d == DAYS_inMonth(this.y, this.m))
        {
            resultObj.d = 0;
            resultObj.m++;
        }
        if (resultObj.m == 12)
        {
            resultObj.m = 0;
            resultObj.y++;
        }
    }

    return resultObj;
}

DateYMD.prototype.getString = DateYMD_getString;
/* get info */
DateYMD.prototype.isValid      = DateYMD_isValid;
DateYMD.prototype.isBefore     = DateYMD_isBefore;
DateYMD.prototype.isAfter      = DateYMD_isAfter;
DateYMD.prototype.diffYMDToYMD = DateYMD_diffYMDToYMD;
/* set */
DateYMD.prototype.setFromYMD   = DateYMD_setFromYMD;
DateYMD.prototype.nextMonth    = DateYMD_nextMonth;
DateYMD.prototype.nextDay      = DateYMD_nextDay;

