
/// memset()
#include <string.h>
/// sprintf()
#include <stdio.h>

#include "rtc.h"
#include "iospi.h"
#include "ioserial.h"

/// RTC register addresses
#define RTC_R_TIME      0x00
#define RTC_R_ALARM     0x07
#define RTC_R_CFG1      0x0E
#define RTC_R_CFG2      0x0F
#define RTC_W_TIME      0x80
#define RTC_W_ALARM     0x87
#define RTC_W_CFG1      0x8E
#define RTC_W_CFG2      0x8F
/// config register macros
#define RTC_CFG1_DEF    0x1C
#define RTC_CFG2_DEF    0xC8
#define RTC_CFG1_ALARM1 0x01
#define RTC_CFG1_ALARM2 0x02
#define RTC_CFG2_BBSQW  0x40
#define RTC_CFG2_AL1F   0x01
#define RTC_CFG2_AL2F   0x02

/// __LOCAL DATA__******************
BYTE AlarmTriggered = FALSE; /// flag get set by ISR
void rtcReset() {

    spiSelectRTC();

    //write to control registers
    spiExchange(RTC_W_CFG1);

    //defaults with alarm1 enable
    spiExchange(RTC_CFG1_DEF | RTC_CFG1_ALARM1);

    //defaults and not battery backed sq.wave
    spiExchange(RTC_CFG2_DEF & ~RTC_CFG2_BBSQW);

    spiDeselectRTC();

    return;
}

void rtcRead(Time* now) {
    int i;
    BYTE temp[7]={0};


    spiSelectRTC();
    spiExchange(RTC_R_TIME);

    for (i = 0; i < 7; i++) {
        temp[i] = spiExchange(0x00);
    }

    spiDeselectRTC();

    //values are in binary coded decimal so
//     * lower half of byte is ones place
//     * upper half is tens with some control bits
    now->tm_sec  = ((temp[0]&0x0F) + 10 * (0x07 & temp[0] >> 4));
    now->tm_min  = (temp[1]&0x0F) + 10 * (temp[1] >> 4);
    now->tm_hour = (temp[2]&0x0F);
    if (temp[2] & 0x40) { //12 hour mode
        now->tm_hour += (temp[2]&0x10) ? 10 : 0;
        now->tm_hour += (temp[2]&0x20) ? 12 : 0;
    } else { //24 hour mode
        now->tm_hour += 10 * (0x03 & temp[2] >> 4);
    }
    now->tm_mday = (temp[4]&0x0F) + 10 * (0x03 & temp[4] >> 4);
    now->tm_mon  = (temp[5]&0x0F) + 10 * (0x01 & temp[5] >> 4) - 1;
    now->tm_year = 100 + (temp[6]&0x0F) + 10 * (temp[6] >> 4);
    //+100 becase struct tm years starts at 1900, rtc starts at 2000

    return;
}

void rtcSetTime(const Time* now) {
    BYTE buff[7]; //[sec,min,hour,week,day,month,year]
    int i;

    buff[0] = ((now->tm_sec / 10) << 4) + now->tm_sec % 10;
    buff[1] = ((now->tm_min / 10) << 4) + now->tm_min % 10;
    buff[2] = ((now->tm_hour / 10) << 4) + now->tm_hour % 10;
    buff[3] = 0; //don't care about day of week
    buff[4] = ((now->tm_mday / 10) << 4) + now->tm_mday % 10;
    buff[5] = (((now->tm_mon + 1) / 10) << 4) + (now->tm_mon + 1) % 10;
    if (100 > now->tm_year) {
        buff[6] = (((now->tm_year) / 10) << 4) + (now->tm_year) % 10;
    } else {
        buff[6] = (((now->tm_year - 100) / 10) << 4) + (now->tm_year - 100) % 10;
    }

    spiSelectRTC();
    spiExchange(RTC_W_TIME); //address first byte, each write goes to next byte
    for (i = 0; i < 7; i++) {
        spiExchange(buff[i]);
    }
    spiDeselectRTC();

    return;
}

void rtcSetAlarm(const Time* alarm) {
    BYTE buff[4];
    int i;
    //first clear alarm bits
    rtcReset();

    buff[0] = ((alarm->tm_sec / 10) << 4) + alarm->tm_sec % 10;
    buff[1] = ((alarm->tm_min / 10) << 4) + alarm->tm_min % 10;
    buff[2] = ((alarm->tm_hour / 10) << 4) + alarm->tm_hour % 10;
    buff[3] = 0x80; //match hour min and second, not day

    spiSelectRTC();
    spiExchange(RTC_W_ALARM); //address first byte, each write goes to next byte
    for (i = 0; i < 4; i++) {
        spiExchange(buff[i]);
    }
    spiDeselectRTC();

    AlarmTriggered = FALSE;
    return;
}

int rtcDidAlarm() {
    BYTE conf;

    spiSelectRTC();
    spiExchange(RTC_R_CFG2);
    conf = spiExchange(0x00);
    spiDeselectRTC();

    if (AlarmTriggered) {
        serPrintf(SER_CONS, "Alarmed, c:%.2x\r\n", conf);
        return (TRUE);
    } else {
        serPrintf(SER_CONS, "NoAlarm, c:%.2x\r\n", conf);
        return (FALSE);
    }

}

/// prints DateTime into buff(no endline)
/// returns pointer to end of string
char* rtcPrint(char* buff, const Time* now) {
    int len;
    //  YYYY/MM/DD hh:mm:ss
    len = sprintf(buff, "%04u-%02u-%02uT%02u:%02u:%02uZ",
            now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
            now->tm_hour, now->tm_min, now->tm_sec);
    return (buff + len);
}

/// no end line (is terminated), no overflow check
char* rtcPrintNow(char* buff) {
    Time now;
    rtcRead(&now);
    return (rtcPrint(buff, &now));
}

time_t rtc_mktime(Time *tm_out) {
    Time now={0};
    Time *tp = (NULL==tm_out ? &now : tm_out);
    rtcRead(tp);
//    return (mktime(tp));
    time_t tt=mktime(tp);
    Time *gm=gmtime(&tt);
    return (mktime(gm));
}

/// time stamp function for file sys
/// fattime format:
/// [00-05] = seconds/2(0-29)
/// [06-11] = minutes(0-60)
/// [12-16] = hours(0-24)
/// [17-21] = day of month(1-31)
/// [22-25] = month(1-12)
/// [26-32] = year since 1980(0-127)
DWORD get_fattime() {
    DWORD temp, fatTime = 0; //temp: so there's space to shift into
    Time now;
    rtcRead(&now);

    temp = now.tm_year - 80;
    fatTime |= 0xFE000000 & (temp << 25);

    temp = now.tm_mon + 1;
    fatTime |= 0x01E00000 & (temp << 21);

    temp = now.tm_mday;
    fatTime |= 0x001F0000 & (temp << 16);

    temp = now.tm_hour;
    fatTime |= 0x0000F800 & (temp << 11);

    temp = now.tm_min;
    fatTime |= 0x000007E0 & (temp << 5);

    temp = now.tm_sec / 2;
    fatTime |= 0x0000001F & (temp << 0);

    return (fatTime);
}


/// --RTC Alarm Interrupt---
/// Full data format used by RTC chip
/// 7 6 5 4 3 2 1 0
/// 00 |X|10sec| sec   |[0-59]
/// 01 |X|10min| min   |[0-59]
/// 02 |X|a b c| hour  |[0-23] a=12/24, b=pm/20hour, c=10 hour
/// 03 |X X X X X|weekD|[1-7]
/// 04 |X X|10s|M day  |[1-31]
/// 05 |a|X X|b|month  |[1-12] a=cent.OF, b=10mon
/// 06 |10year |year   |[0-99] (0=1900)

#ifndef USE_MCC_EXT_INT_ISR
 void EX_INT0_CallBack(void)
{
    // Add your custom callback code here
    AlarmTriggered = TRUE;
}
/*
void __attribute__((interrupt, no_auto_psv)) _INT0Interrupt(void) {
    AlarmTriggered = TRUE;
    //do something?
//    Int0_Clear_Intr_Status_Bit;
    P24_INT0_CLR_INT_STATUS_BIT();
}
*/

#else
#warning "RTC ISR replaced with MCC code"
#endif /// USE_MCC_EXT_INT_ISR