/*
 * hms - calculate in hours, minutes, and seconds
 *
 * Copyright (C) 2010  Landon Curt Noll
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @(#) $Revision: 30.2 $
 * @(#) $Id: hms.cal,v 30.2 2010/09/02 06:14:16 chongo Exp $
 * @(#) $Source: /usr/local/src/bin/calc/cal/RCS/hms.cal,v $
 *
 * Under source code control:	2010/09/01 17:14:55
 * File existed as early as:	2010
 *
 * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/
 */


obj hms {hour, min, sec};

define hms(hour, min, sec)
{
    local obj hms ans;		/* return value */

    /* default missing args to 0 */
    if (isnull(sec)) {
	sec = 0;
    }
    if (isnull(min)) {
	min = 0;
    }

    /* load object */
    ans.hour = hour;
    ans.min = min;
    ans.sec = sec;

    /* return properly formed object */
    ans = fixhms(ans);
    return ans;
}


define hms_add(a, b)
{
    local obj hms ans;		/* return value */

    /* initalize value to 1st arg */
    if (istype(a, ans)) {
	/* 1st arg is hms object, load it */
	ans.hour = a.hour;
	ans.min = a.min;
	ans.sec = a.sec;
    } else {
	/* 1st arg is not hms, assume scalar hours */
	ans.hour = a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* add value of 2nd arg */
    if (istype(b, ans)) {
	/* 2nd arg is hms object, add it */
	ans.hour += b.hour;
	ans.min += b.min;
	ans.sec += b.sec;
    } else {
	/* 2nd arg is not hms, add scalar hours */
	ans.hour += b;
    }

    /* return normalized result */
    ans = fixhms(ans);
    return ans;
}


define hms_neg(a)
{
    local obj hms ans;		/* return value */

    /* negate argument */
    if (istype(a, ans)) {
	/* 1st arg is hms object, load it */
	ans.hour = -a.hour;
	ans.min = -a.min;
	ans.sec = -a.sec;
    } else {
	/* 2nd arg is not hms, negate scalar hours */
	ans.hour = -a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* return normalized result */
    ans = fixhms(ans);
    return ans;
}


define hms_sub(a, b)
{
    local obj hms ans;		/* return value */

    /* initalize value to 1st arg */
    if (istype(a, ans)) {
	/* 1st arg is hms object, load it */
	ans.hour = a.hour;
	ans.min = a.min;
	ans.sec = a.sec;
    } else {
	/* 1st arg is not hms, assume scalar hours */
	ans.hour = a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* subtract value of 2nd arg */
    if (istype(b, ans)) {
	/* 2nd arg is hms object, subtract it */
	ans.hour -= b.hour;
	ans.min -= b.min;
	ans.sec -= b.sec;
    } else {
	/* 2nd arg is not hms, subtract scalar hours */
	ans.hour -= b;
    }

    /* return normalized result */
    ans = fixhms(ans);
    return ans;
}


define hms_mul(a, b)
{
    local obj hms ans;		/* return value */

    /* hms object multiplication */
    if (istype(a, ans) && istype(b, ans)) {
	ans.hour = hms_abs(a) * hms_abs(b);
	ans.min = 0;
	ans.sec = 0;

    /* scalar multiplication */
    } else if (istype(a, ans)) {
	ans.hour = a.hour * b;
	ans.min = a.min * b;
	ans.sec = a.sec * b;
    } else {
	ans.hour = b.hour * a;
	ans.min = b.min * a;
	ans.sec = b.sec * a;
    }

    /* return normalized result */
    ans = fixhms(ans);
    return ans;
}


define hms_print(a)
{
    local obj hms ans;		/* temp object for hms type testing */

    /* firewall - arg must be a hms object */
    if (! istype(a, ans)) {
	quit "hms_print called with non hms object";
    }

    /* print in hms form */
    print a.hour : ':' : a.min : ':' : a.sec :;
}


define hms_abs(a)
{
    local obj hms ans;		/* temp object for hms type testing */
    local hour;			/* return scalar value */

    /* firewall - just absolute value non hms objects */
    if (! istype(a, ans)) {
    	return abs(a);
    }

    /* compute hours */
    hour = a.hour + a.min / 60 + a.sec / 3600;

    /* return hours */
    return hour;
}


define hms_norm(a)
{
    local obj hms ans;		/* temp object for hms type testing */
    local hour;			/* hours */

    /* firewall - arg must be a hms object */
    if (! istype(a, ans)) {
	quit "hms_norm called with non hms object";
    }

    /* square hours (norm is the square of absolute value */
    hour = hms_abs(a);

    /* return hours */
    return hour*hour;
}


define hms_test(a)
{
    local obj hms ans;		/* temp value */

    /* firewall - arg must be a hms object */
    if (! istype(a, ans)) {
	quit "hms_test called with non hms object";
    }

    /* return false of non-zero */
    ans = fixhms(a);
    if (ans.hour == 0 && ans.min == 0 && ans.sec == 0) {
	/* false */
	return 0;
    }
    /* true */
    return 1;
}


define hms_int(a)
{
    local obj hms ans;		/* return value */

    /* firewall - arg must be a hms object */
    if (! istype(a, ans)) {
	quit "hms_int called with non hms object";
    }

    /* normalize the argument */
    ans = fixhms(a);

    /* truncate to the nearest second */
    ans.sec = int(ans.sec);

    /* return value to the nearest second */
    return ans;
}


define hms_frac(a)
{
    local obj hms ans;		/* return value */

    /* firewall - arg must be a hms object */
    if (! istype(a, ans)) {
	quit "hms_frac called with non hms object";
    }

    /* normalize the argument */
    ans = fixhms(a);

    /* remove all but fractional seconds */
    ans.hour = 0;
    ans.min = 0;
    ans.sec = frac(ans.sec);

    /* return value to the second fraction */
    return ans;
}


define hms_rel(a,b)
{
    local abs_a, abs_b;		/* scalars of the arguments */

    /* compute scalars of the arguments */
    abs_a = hms_abs(a);
    abs_b = hms_abs(b);

    /* return the comparison */
    return cmp(abs_a, abs_b);
}


define hms_cmp(a,b)
{
    local abs_a, abs_b;		/* scalars of the arguments */

    /* compute scalars of the arguments */
    abs_a = hms_abs(a);
    abs_b = hms_abs(b);

    /* return the equality comparison */
    return (abs_a == abs_b);
}


define hms_inc(a)
{
    local obj hms ans;		/* return value */

    /* increment a hms object */
    if (istype(a, ans)) {
	ans = a;
	++ans.sec;

	/* return normalized result */
	ans = fixhms(ans);
	return ans;
    }

    /* increment a scalar */
    return a+1;
}


define hms_dec(a)
{
    local obj hms ans;		/* return value */

    /* decrement a hms object */
    if (istype(a, ans)) {
	ans = a;
	--ans.sec;

	/* return normalized result */
	ans = fixhms(ans);
	return ans;
    }

    /* decrement a scalar */
    return a-1;
}


define fixhms(a)
{
    local obj hms ans;		/* temp value */

    /* firewall */
    if (! istype(a, ans)) {
	quit "attempt to fix a non hms object";
    }

    /* force minutes to be intergral */
    a.min += frac(a.hour) * 60;
    a.hour = int(a.hour);

    /* force hours to be intergral */
    a.sec += frac(a.min) * 60;
    a.min = int(a.min);

    /* carry excess seconds into minutes */
    a.min += a.sec // 60;
    a.sec %= 60;

    /* carry excess minutes into hours */
    a.hour += a.min // 60;
    a.min %= 60;

    /* round hours by day */
    a.hour %= 24;

    /* return normalized result */
    return a;
}

if (config("resource_debug") & 3) {
    print "obj hms {hour, min, sec} defined";
}
