//#DOC Main, program entry and main loop
#include "main.h"
#include <otMCP23017.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otFPGA.h>
#include <otI2cBus.h>
#include <otPrintfLite.h>
#include <otPWM.h>
#include <otRTC.h>

enum rgbIndex {
    RGB_RED,
    RGB_GREEN,
    RGB_BLUE
};

// For time measure
#define TimeMark(n) _timeMark[n] = TimerGetCounts(TIMER0)
#define TimeMeasure(n) ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0))

enum TIME_PROCESS_ID {
    TL_BLINK,           // Blinking LED
    TL_UPDATE,          // Time update
};


otMCP23017  port;
otPWM       pwm;

U16         rgbLed[10][3];
U8          nixie[6];
U16         ledPort = 0;
U16         ledIndex = 0;
U16         nixieIndex = 0;
U16         ledPower = 128;

U32         _timeMark[16];

bool TimeCheckUs(U8 n, U32 val)
{
    bool    ret = false;
    U32     et = ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0));
    if(et >= val) ret = true;
    return ret;
}

bool TimeCheck(U8 n, U32 val)
{
    bool    ret = false;
    U32     et = ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0)) / 1000;
    if(et >= val) ret = true;
    return ret;
}

void ledDisplay()
{
    // Blank LED selecting un available
    port.write(ledPort | 0x00F0);
    //Set new RGB value
    pwm.setPin(RGB_RED, rgbLed[ledIndex][RGB_RED]);
    pwm.setPin(RGB_GREEN, rgbLed[ledIndex][RGB_GREEN]);
    pwm.setPin(RGB_BLUE, rgbLed[ledIndex][RGB_BLUE]);
    ledPort &= 0xFF0F;
    ledPort |= (ledIndex << 4);
    ledIndex++;
    if(ledIndex == 10) ledIndex = 0;
}

void nixieDisplay()
{
    U16     nixieBit = 0x100;
    // Blank current nixie
    port.write(ledPort | 0x000F);
    nixieBit <<= nixieIndex;
    ledPort &= 0x00F0;
    ledPort |= nixieBit;
    ledPort |= nixie[nixieIndex];
    nixieIndex++;
    if(nixieIndex == 6) nixieIndex = 0;
}

void ledNixie(U16 red, U16 green, U16 blue)
{
    for(int i = 4; i < 10; i++)
    {
        rgbLed[i][RGB_RED] = red;
        rgbLed[i][RGB_GREEN] = green;
        rgbLed[i][RGB_BLUE] = blue;
    }
}

void getTime()
{
    char    buf[8];
    U8      h, m, s;
    //int     yy, mm, dd;
    RtcGetTime(&h, &m, &s);
    //RtcGetDate(&yy, &mm, &dd);
    
    sprintf(buf, "%02d", h);
    nixie[0] = buf[0] - '0';
    nixie[1] = buf[1] - '0';
    
    sprintf(buf, "%02d", m);
    nixie[2] = buf[0] - '0';
    nixie[3] = buf[1] - '0';
    
    sprintf(buf, "%02d", s);
    nixie[4] = buf[0] - '0';
    nixie[5] = buf[1] - '0';
}

void setup()
{
    // Setup the UART0 used by printf
    UartEnableSignals(UART0);
    UartSetBaud(UART0, 115200);
    // Init printf
    setStdout(UART0); // printf output is now directed on UART0
    
    RtcOn();
    // This test has been written in this date
    RtcSetDate(2022, 2, 11);
    RtcSetTime(0, 0, 0);
    
    FpgaInit();
    FpgaHiddenPort(HP_EXT_I2C, true);
    
    I2cBusReset();
    I2cBusFrequency(100, 30);
    
    TimerSetAsCounter(TIMER0);
    TimerEnable(TIMER0, true);
    
    DelayMs(1000);
    
    port.init(7);
    
    if(pwm.init() == false)
    {
        printf("Error on PWM init\n");
        IDLE;
    }
    pwm.setPWMFreq(1500);
    pwm.setOutputMode(true);
    pwm.setPin(0, 0);
    pwm.setPin(1, 0);
    pwm.setPin(2, 0);
    
    port.pinMode(0, AS_OUTPUT);
    port.pinMode(1, AS_OUTPUT);
    port.pinMode(2, AS_OUTPUT);
    port.pinMode(3, AS_OUTPUT);
    port.pinMode(4, AS_OUTPUT);
    port.pinMode(5, AS_OUTPUT);
    port.pinMode(6, AS_OUTPUT);
    port.pinMode(7, AS_OUTPUT);
    
    port.pinMode(8, AS_OUTPUT);
    port.pinMode(9, AS_OUTPUT);
    port.pinMode(10, AS_OUTPUT);
    port.pinMode(11, AS_OUTPUT);
    port.pinMode(12, AS_OUTPUT);
    port.pinMode(13, AS_OUTPUT);
    
    // Blank LEDs
    for(int i = 0; i < 10; i++)
    {
        rgbLed[i][RGB_RED] = 0;
        rgbLed[i][RGB_GREEN] = 0;
        rgbLed[i][RGB_BLUE] = 0;
    }
   
    // Blank nixie
    for(int i = 0; i < 6; i++)
        nixie[i] = 0xF;
    
    ledNixie(0, 0, ledPower);
    rgbLed[0][RGB_RED] = ledPower;
    rgbLed[1][RGB_RED] = ledPower;
    
    printf("Started ...\n");
    
    // Store timer data
    TimeMark(TL_BLINK);
    TimeMark(TL_UPDATE);
}

void loop()
{
    // Update LED
    ledDisplay();
    nixieDisplay();
    port.write(ledPort);
    DelayUs(500);

    // Blink every second
    if(TimeCheck(TL_BLINK, 500))
    {
        TimeMark(TL_BLINK);
        if(!rgbLed[0][RGB_RED])
        {
            rgbLed[0][RGB_RED] = ledPower;
            rgbLed[1][RGB_RED] = ledPower;
        }
        else
        {
            rgbLed[0][RGB_RED] = 0;
            rgbLed[1][RGB_RED] = 0;
        }
    }
    
    if(TimeCheck(TL_UPDATE, 1000))
    {
        TimeMark(TL_UPDATE);
        getTime();
    }
}