#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/family/arm/cc26xx/Power.h>
#include <ti/drivers/PIN.h>
#include <string.h>
#include <inc/hw_ints.h>
#include "ICall.h"
#include "Macs.h"
#include "MacStackMSG.h"
#include <aon_event.h>
#include <ioc.h>
#include "Board.h"

#include "watchdogp.h"
#include "epdwatchdog.h"
extern uint32_t halTime2Ticks(uint32 ms);

WatchdogCC26XX_Object watchdogCC26XXObjects[CC26X2R1_LAUNCHXL_WATCHDOGCOUNT];

const WatchdogCC26XX_HWAttrs watchdogCC26XXHWAttrs[CC26X2R1_LAUNCHXL_WATCHDOGCOUNT] = {
    {
        .baseAddr    = WDT_BASE,
        .reloadValue = WDT_TIMEOUT_MS /* Reload value in milliseconds */
    },
};

const Watchdog_Config Watchdog_config[CC26X2R1_LAUNCHXL_WATCHDOGCOUNT] = {
    {
        .fxnTablePtr = &WatchdogCC26XX_fxnTable,
        .object      = &watchdogCC26XXObjects[CC26X2R1_LAUNCHXL_WATCHDOG0],
        .hwAttrs     = &watchdogCC26XXHWAttrs[CC26X2R1_LAUNCHXL_WATCHDOG0]
    },
};

const uint_least8_t Watchdog_count = CC26X2R1_LAUNCHXL_WATCHDOGCOUNT;

/* Default Watchdog parameters structure */
const Watchdog_Params Watchdog_defaultParams = {
    NULL,                    /* callbackFxn */
    Watchdog_RESET_ON,       /* resetMode */
    Watchdog_DEBUG_STALL_ON, /* debugStallMode */
    NULL                     /* custom */
};

static bool isInitialized = false;

/*
 *  ======== Watchdog_clear ========
 */
void Watchdog_clear(Watchdog_Handle handle)
{
    handle->fxnTablePtr->watchdogClear(handle);
}

/*
 *  ======== Watchdog_close ========
 */
void Watchdog_close(Watchdog_Handle handle)
{
    handle->fxnTablePtr->watchdogClose(handle);
}

/*
 *  ======== Watchdog_control ========
 */
int_fast16_t Watchdog_control(Watchdog_Handle handle, uint_fast16_t cmd,
                              void *arg)
{
    return (handle->fxnTablePtr->watchdogControl(handle, cmd, arg));
}

/*
 *  ======== Watchdog_init ========
 */
void Watchdog_init(void)
{
    uint_least8_t i;
    uint_fast32_t key;

    key = Hwi_disable();

    if (!isInitialized) {
        isInitialized = (bool) true;

        /* Call each driver's init function */
        for (i = 0; i < Watchdog_count; i++) {
            Watchdog_config[i].fxnTablePtr->watchdogInit((Watchdog_Handle)&(Watchdog_config[i]));
        }
    }

    Hwi_restore(key);
}

/*
 *  ======== Watchdog_open ========
 */
Watchdog_Handle Watchdog_open(uint_least8_t index, Watchdog_Params *params)
{
    Watchdog_Handle handle = NULL;

    /* Verify driver index and state */
    if (isInitialized && (index < Watchdog_count)) {
        /* If params are NULL use defaults */
        if (params == NULL) {
            params = (Watchdog_Params *) &Watchdog_defaultParams;
        }

        handle = (Watchdog_Handle)&(Watchdog_config[index]);
        handle = handle->fxnTablePtr->watchdogOpen(handle, params);
    }

    return (handle);
}

/*
 *  ======== Watchdog_Params_init ========
 */
void Watchdog_Params_init(Watchdog_Params *params)
{
    *params = Watchdog_defaultParams;
}


/*
 *  ======== Watchdog_setReload ========
 */
int_fast16_t Watchdog_setReload(Watchdog_Handle handle, uint32_t ticks)
{
    return (handle->fxnTablePtr->watchdogSetReload(handle, ticks));
}

/*
 *  ======== Watchdog_convertMsToTicks ========
 */
uint32_t Watchdog_convertMsToTicks(Watchdog_Handle handle, uint32_t milliseconds)
{
    return (handle->fxnTablePtr->watchdogConvertMsToTicks(handle, milliseconds));
}

/* Watchdog function table for CC26XX implementation */
const Watchdog_FxnTable WatchdogCC26XX_fxnTable = {
    WatchdogCC26XX_clear,
    WatchdogCC26XX_close,
    WatchdogCC26XX_control,
    WatchdogCC26XX_init,
    WatchdogCC26XX_open,
    WatchdogCC26XX_setReload,
    WatchdogCC26XX_convertMsToTicks
};

/* Maximum allowable setReload value */
#define MAX_RELOAD_VALUE        0xFFFFFFFF
#define WATCHDOG_DIV_RATIO      32            /* Watchdog division ratio */
#define MS_RATIO                1000          /* millisecond to second ratio */

/*
 *  ======== WatchdogCC26XX_clear ========
 */
void WatchdogCC26XX_clear(Watchdog_Handle handle)
{
    WatchdogIntClear();
}

/*
 *  ======== WatchdogCC26XX_close ========
 */
void WatchdogCC26XX_close(Watchdog_Handle handle)
{
    /*
     *  Not supported for CC26XX - Once the INTEN bit of the WDTCTL
     *  register has been set, it can only be cleared by a hardware
     *  reset.
     */
//    DebugP_assert(false);
}

/*
 *  ======== WatchdogCC26XX_control ========
 *  @pre    Function assumes that the handle is not NULL
 */
int_fast16_t WatchdogCC26XX_control(Watchdog_Handle handle, uint_fast16_t cmd,
        void *arg)
{
    /* No implementation yet */
    return (Watchdog_STATUS_UNDEFINEDCMD);
}

/*
 *  ======== Watchdog_init ========
 */
void WatchdogCC26XX_init(Watchdog_Handle handle)
{
    WatchdogCC26XX_Object *object = handle->object;

    object->isOpen = false;
}

/*
 *  ======== WatchdogCC26XX_open ========
 */
Watchdog_Handle WatchdogCC26XX_open(Watchdog_Handle handle, Watchdog_Params *params)
{
    unsigned int                   key;
//    HwiP_Params                     hwiParams;
    WatchdogCC26XX_Object         *object;

    /* get the pointer to the object and hwAttrs */
    object = handle->object;

    /* disable preemption while checking if the WatchDog is open. */
    key = Hwi_disable();

    /* Check if the Watchdog is open already with the HWAttrs */
    if (object->isOpen == true) {
        Hwi_restore(key);
//        DebugP_log1("Watchdog: Handle %x already in use.", (uintptr_t)handle);
        return (NULL);
    }

    object->isOpen = true;
    Hwi_restore(key);

    /* initialize the Watchdog object */
    object->debugStallMode = params->debugStallMode;
    object->resetMode      = params->resetMode;

    /* Construct Hwi object for Watchdog */
    /*
    HwiP_Params_init(&hwiParams);
    hwiParams.arg = (uintptr_t)handle;
    */

    /* setup callback function if defined */
    if (params->callbackFxn != NULL) {
        Hwi_plug(INT_NMI_FAULT, (void *)params->callbackFxn);
    }

    /* initialize the watchdog hardware */
    WatchdogCC26XX_initHw(handle);

//    DebugP_log1("Watchdog: handle %x opened" ,(uintptr_t)handle);

    /* return handle of the Watchdog object */
    return (handle);
}

/*
 *  ======== WatchdogCC26XX_setReload ========
 */
int_fast16_t WatchdogCC26XX_setReload(Watchdog_Handle handle, uint32_t ticks)
{
    unsigned int                   key;

    /* disable preemption while unlocking WatchDog registers */
    key = Hwi_disable();

    /* unlock the Watchdog configuration registers */
    WatchdogUnlock();

    /* make sure the Watchdog is unlocked before continuing */
    while(WatchdogLockState() == WATCHDOG_LOCK_LOCKED)
    { }

    /* update the reload value */
    WatchdogReloadSet(ticks);

    /* lock register access */
    WatchdogLock();

    Hwi_restore(key);

//    DebugP_log2("Watchdog: WDT with handle 0x%x has been set to "
//        "reload to 0x%x", (uintptr_t)handle, ticks);

    return (Watchdog_STATUS_SUCCESS);
}

/*
 *  ======== WatchdogCC26XX_hwInit ========
 *  This function initializes the Watchdog hardware module.
 *
 *  @pre    Function assumes that the Watchdog handle is pointing to a hardware
 *          module which has already been opened.
 */
void WatchdogCC26XX_initHw(Watchdog_Handle handle) {
    unsigned int                   key;
    uint32_t                       tickValue;
    WatchdogCC26XX_Object          *object;
    WatchdogCC26XX_HWAttrs const   *hwAttrs;

    /* get the pointer to the object and hwAttrs */
    object = handle->object;
    hwAttrs = handle->hwAttrs;

    /* convert milliseconds to watchdog timer ticks */
    tickValue = WatchdogCC26XX_convertMsToTicks(handle, hwAttrs->reloadValue);

    /* disable preemption while unlocking WatchDog registers */
    key = Hwi_disable();

    /* unlock the Watchdog configuration registers */
    WatchdogUnlock();

    /* make sure the Watchdog is unlocked before continuing */
    while(WatchdogLockState() == WATCHDOG_LOCK_LOCKED)
    { }

    WatchdogReloadSet(tickValue);

    /* set reset mode */
    if (object->resetMode == Watchdog_RESET_ON) {
        WatchdogResetEnable();
    }
    else {
        WatchdogResetDisable();
    }

    /* set debug stall mode */
    if (object->debugStallMode == Watchdog_DEBUG_STALL_ON) {
        WatchdogStallEnable();
    }
    else {
        WatchdogStallDisable();
    }

    /* enable the Watchdog interrupt as a non-maskable interrupt */
    WatchdogIntTypeSet(WATCHDOG_INT_TYPE_NMI);

    /* enable the Watchdog */
    WatchdogEnable();

    /* lock the Watchdog configuration registers */
    WatchdogLock();

    Hwi_restore(key);
}

/*
 *  ======== WatchdogCC26XX_convertMsToTicks ========
 *  This function converts the input value from milliseconds to
 *  Watchdog clock ticks.
 */
uint32_t WatchdogCC26XX_convertMsToTicks(Watchdog_Handle handle,
    uint32_t milliseconds)
{
    uint32_t wdtTicks;

    wdtTicks = milliseconds*8;
//    wdtTicks = milliseconds;
    wdtTicks = halTime2Ticks(wdtTicks);
    return wdtTicks;
}
    
Watchdog_Handle watchdogHd = NULL;
void epdWatchdogClear(void)
{
    if(watchdogHd)
    {
        Watchdog_clear(watchdogHd);
    }
}

void epdWatchdogStart(void)
{
    Watchdog_Params params;
    uint32_t timeout;

    Watchdog_init();
    Watchdog_Params_init(&params);
    params.resetMode = Watchdog_RESET_ON;

    watchdogHd = Watchdog_open(0, &params);

    if(NULL != watchdogHd)
    {
        timeout = WatchdogCC26XX_convertMsToTicks(watchdogHd, WDT_TIMEOUT_MS);
        Watchdog_setReload(watchdogHd, timeout);
    }
}

