#include <xdc/runtime/System.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>

#include "TRF7970A.h"

extern Clock cl0;
extern Clock cl1;
extern Clock cl2;
extern Clock cl3;

extern Task_Struct task0Struct, task1Struct;
extern Char task0Stack[TASKSTACKSIZE], task1Stack[TASKSTACKSIZE];
extern Semaphore_Struct sem0Struct, sem1Struct;
extern Semaphore_Handle sem0Handle, sem1Handle;
extern Clock_Struct clk0Struct, clk1Struct;

/*
 *  ======== clockTerminate ========
 *  This function simply terminates the example
 */
const char *months[12] = { "January", "February", "March",
                           "April",   "May",      "June",
                           "July",    "August",   "September",
                           "October", "November", "December" };

void clockTerminate(UArg arg)
{
    System_printf("bigTime ended.\n");
    BIOS_exit(0);
}

/*
 *  ======== clockTask ========
 *  Wrapper function for Task objects calling
 *  Clock::tick()
 */
void clockTask(UArg arg)
{
    Clock *clock = (Clock *)arg;
    int count = 0;

    if (clock->getId() == 3) {
        for(;;) {             // task id = 3
            Semaphore_pend(sem0Handle, BIOS_WAIT_FOREVER);
            clock->tick();
            if(count == 50) {
                Task_sleep(25);
                count = 0;
            }
            count++;
            Semaphore_post(sem1Handle);
        }
    }
    else {
        for(;;) {             // task id = 4
            Semaphore_pend(sem1Handle, BIOS_WAIT_FOREVER);
            if(count == 50) {
                Task_sleep(25);
                count = 0;
            }
            clock->tick();
            count++;
            Semaphore_post(sem0Handle);
        }
    }
}

/*
 * ======== clockPrd ========
 * Wrapper function for PRD objects calling
 * Clock::tick()
 */
void clockPrd(UArg arg)
{
    Clock *clock = (Clock *)arg;

    clock->tick();
    return;
}

/*
 * ======== clockIdle ========
 * Wrapper function for IDL objects calling
 * Clock::tick()
 * It also calls System_flush() to periodically
 * print the contents in the SysMin buffer
 */
void clockIdle(void)
{
    cl0.tick();
    System_flush();
    return;
}


/*
 * Clock methods
 */
Clock::Clock(int newId)
{
    id = newId;
    ticks = 0;
    microsecond = 0;
    millisecond = 0;
    second = 0;
    minute = 0;
    hour = 0;
    day = 19;
    month = 8;
    year = 10;
    century = 20;
    millenium = 0;
}

Clock::~Clock()
{
}

void Clock::tick()
{
    ticks++;

    if (getId() == 1) {
        System_printf("id %d : %d:%d:%d.%d\n", getId(), hour, minute, second, millisecond / 100);
        System_printf("id %d : %s %d, %d%d\n", getId(), (IArg)months[month-1], day, century, year);
        /*
         * id 1 expires every 100 ticks (and each tick is 1 millisecond)
         */
        setMillisecond(100);
    }
    if (getId() == 2) {
        System_printf("id %d : %d:%d:%d\n", getId(), hour, minute, second);
        System_printf("id %d : %s %d, %d%d\n", getId(), (IArg)months[month-1], day, century, year);
        /*
         * Change selected function to alter clock rate
         */
//      setMicrosecond();
//      setMillisecond();
        setSecond();
//      setMinute();
//      setDay();
        if (ticks == 2) {
            clockTerminate(0);
        }
    }

    return;
}

void Clock::setMicrosecond()
{
    if (microsecond >= 999) {
        setMillisecond();
        microsecond = 0;
    }
    else {
        microsecond++;
    }

    return;
}

void Clock::setMillisecond()
{
    if (millisecond >= 999) {
        setSecond();
        millisecond = 0;
    }
    else {
        millisecond++;
    }

    return;
}

void Clock::setMillisecond(int nMilliseconds)
{
    int secs;

    millisecond += nMilliseconds;
    secs = millisecond / 1000;
    millisecond %= 1000;

    while (secs--) {
        setSecond();
    }

    return;
}

void Clock::setSecond()
{
    if (second == 59) {
        setMinute();
        second = 0;
    }
    else {
        second++;
    }

    return;
}

void Clock::setMinute()
{
    if (minute == 59) {
        setHour();
        minute = 0;
    }
    else {
        minute++;
    }

    return;
}

void Clock::setHour()
{
    if (hour == 23) {
        setDay();
        hour = 0;
    }
    else {
        hour++;
    }

    return;
}

void Clock::setDay()
{
    bool thirtydays = false;
    bool feb = false;
    bool leap = false;

    if (month == 4 || month == 6 || month == 9 || month == 11) {
        // April, June, September, November.
        thirtydays = true;
    }

    if (month == 2) {  // Test for February
        feb = true;
    }

    /*
     * A year is a leap year if it is divisible by 4, but not by 100.
     *
     * If a year is divisible by 4 and by 100, it is a leap year only
     * if it is also divisible by 400.
     */
    if ((year%4 == 0 && year%100 != 0) ||
            (year%4 == 0 && year%100 == 0 && year%400 == 0)) {
        leap = true;
    }

    if ((day == 28) && (feb) && (!leap)) {
        setMonth();
        day = 1;
    }
    else if ((day == 29) && (feb) && (leap)) {
        setMonth();
        day = 1;
    }
    else if ((day == 30) && (thirtydays == true)) {
        setMonth();
        day = 1;
    }
    else if ((day == 31) && (thirtydays == false)) {
        setMonth();
        day = 1;
    }
    else {
        day++;
    }

    return;
}

void Clock::setMonth()
{
    if (month >= 12) {
        setYear();
        month = 1;
    }
    else {
        month++;
    }

    return;
}

void Clock::setYear()
{
    year++;
    if ((year%100) == 0) {
        setCentury();
    }

    return;
}

void Clock::setCentury()
{
    century++;
    if ((century%10) == 0) {
        setMillenium();
    }

    return;
}

void Clock::setMillenium()
{
    millenium++;

    return;
}

long Clock::getTicks()
{
    return ticks;
}

int Clock::getId()
{
    return id;
}

int Clock::getMicrosecond()
{
    return microsecond;
}

int Clock::getMillisecond()
{
    return millisecond;
}

int Clock::getSecond()
{
    return second;
}

int Clock::getMinute()
{
    return minute;
}

int Clock::getHour()
{
    return hour;
}

int Clock::getDay()
{
    return day;
}

int Clock::getMonth()
{
    return month;
}

int Clock::getYear()
{
    return year;
}

int Clock::getCentury()
{
    return century;
}

int Clock::getMillenium()
{
    return millenium;
}
