/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Error.h>
/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/hal/Timer.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Swi.h>
#include <ti/sysbios/knl/Queue.h>
#include <ti/sysbios/hal/Hwi.h>
/* TI-RTOS Header files */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/UART.h>
#include <ti/drivers/uart/UARTTiva.h>
/* Board Header file */
#include "Board.h"
#include <string.h>
#include "driverlib/fpu.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/uart.h"
#include "driverlib/gpio.h"
#include "utils/ustdlib.h"
#include "grlib/grlib.h"
#include "grlib/widget.h"
#include "grlib/canvas.h"
#include "drivers/Kentec320x240x16_ssd2119_spi.h"

#define TASKSTACKSIZE   512

typedef struct MsgObj {
    Queue_Elem elem; /* first field for Queue */
    Char data; /* message value */
} MsgObj;


Task_Struct task0Struct;
Char task0Stack[TASKSTACKSIZE];
Semaphore_Struct sem0Struct;
Semaphore_Handle semHandle;
Event_Struct evtStruct;
Event_Handle evtHandle;
Queue_Handle QueueHandle;
Hwi_Handle HwiUART;

uint32_t g_ui32SysClock;
tContext sContext;

void UARTSend(const uint8_t *pui8Buffer, uint32_t ui32Count) {
    // Loop while there are more characters to send.
    while (ui32Count--) {
        // Write the next character to the UART.
        if (!UARTCharPutNonBlocking(UART0_BASE, *pui8Buffer++)) {
            SysCtlDelay((0.002 * g_ui32SysClock) / 3);
            UARTCharPutNonBlocking(UART0_BASE, *pui8Buffer++);
        }
    }
}

void uart0FuncTask() {

    MsgObj *msg;
    UInt events;

    msg = Queue_get(QueueHandle);
    System_printf("RET msg rec: %c\n", msg->data);

    System_printf("\nmsg uart0FuncTask: %c\n");

    while (1) {
        System_printf("\nmsg while 1\n");
        events = Event_pend(evtHandle, Event_Id_05, Event_Id_NONE, BIOS_WAIT_FOREVER);
        Semaphore_pend(semHandle, BIOS_NO_WAIT);
        if (events & Event_Id_05) {
            Semaphore_pend(semHandle, BIOS_NO_WAIT);
            while (!Queue_empty(QueueHandle)) {
               msg = Queue_get(QueueHandle);
               System_printf("RET msg rec: %c\n", msg->data);
            }
        }
    }
}

void uart0FuncHwi(UArg arg) {

    System_printf("\nHwi: uart0FuncHwi called\n");
    uint32_t ui32Status;
    // Get the interrrupt status.
    ui32Status = UARTIntStatus(UART0_BASE, true);
    // Clear the asserted interrupts.
    UARTIntClear(UART0_BASE, ui32Status);

    char ch;

    // Loop while there are characters in the receive FIFO.
    while (UARTCharsAvail(UART0_BASE)) {
        MsgObj msg;
        ch = UARTCharGetNonBlocking(UART0_BASE); //HWI
        // Read the next character from the UART and write it back to the UART.
        // Blink the LED to show a character transfer is occuring.
        GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, GPIO_PIN_0);
        // Delay for 1 millisecond.  Each SysCtlDelay is about 3 clocks.
        SysCtlDelay(g_ui32SysClock / (1000 * 3));
        // Turn off the LED
        GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, 0);

        if (ch != '\r' && ch != ' ') {
            // Read the next character from the UART and write it back to the UART.
            System_printf("\n msg : %c\n", ch);
            msg.data = ch;
            Queue_put(QueueHandle, &(msg.elem));
            Semaphore_post(semHandle);
        }
        else if (ch == '\r') {
            System_printf("\nmsg with return\n");
            Event_post(evtHandle, Event_Id_05);
        }
    }
}

void initUARTHwiInt() {

    /* Turn on user LED */
    GPIO_write(Board_LED0, Board_LED_ON);
    Hwi_Params hwiParams;
    Error_Block hwi_eb;
    Error_init(&hwi_eb);

    Hwi_Params_init(&hwiParams);
    hwiParams.arg = (UArg)QueueHandle;
    HwiUART = Hwi_create(21, (Hwi_FuncPtr)uart0FuncHwi, &hwiParams, &hwi_eb);
    if (HwiUART == NULL) System_printf("Hwi create failed\n");
}

void initUARTEvent() {
    Event_construct(&evtStruct, NULL);
    evtHandle = Event_handle(&evtStruct);
}

void initUARTSemaphore() {
    Semaphore_Params semParams;
    Semaphore_Params_init(&semParams);
    semParams.mode = Semaphore_Mode_BINARY;
    Semaphore_construct(&sem0Struct, 0, &semParams);
    semHandle = Semaphore_handle(&sem0Struct);
}

void initUARTQueue() {
    QueueHandle = Queue_create(NULL, NULL);
}

void initUARTTaskInt() {
    Task_Params taskParams;
    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    Task_create((Task_FuncPtr)uart0FuncTask, &taskParams, NULL);
}

void configureUART(void) {
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    IntMasterEnable();
    // Configure the UART for 115,200, 8-N-1 operation.
    UARTConfigSetExpClk(UART0_BASE, g_ui32SysClock, 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    // Enable the UART interrupt.
    IntEnable(INT_UART0);
    UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
    System_printf("UART is set\n");
}

void configureGUI(uint32_t g_ui32SysClock) {
    FPUEnable();
    FPULazyStackingEnable();
    Kentec320x240x16_SSD2119Init(g_ui32SysClock);
    GrContextInit(&sContext, &g_sKentec320x240x16_SSD2119);
    GrContextForegroundSet(&sContext, ClrWhite);
    GrContextFontSet(&sContext, &g_sFontCm20);
}


/* main */
int main(void) {

    // Set the clocking to run directly from the crystal at 120MHz.
    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

    /* Call board init functions */
    Board_initGeneral();
    Board_initGPIO();
    Board_initUART();
    configureGUI(g_ui32SysClock);
    initUARTQueue();

    configureUART();
    initUARTTaskInt();
    initUARTHwiInt();
    initUARTEvent();
    initUARTSemaphore();

    /* SysMin will only print to the console when you call flush or exit */
    System_flush();
    /* Start BIOS */
    BIOS_start();

    return (0);
}
