#include "hart_lib.h"
#include "hart_flash.h"
#include "../app.h"
#include "../fsm.h"
#include "../time.h"
#include "../gpio.h"

//=========================== defines =========================================

#define ERROR_OUT_LEN   (0 + 1) // just error RC, no data

//==== Common tables HCF_SPEC-183

// Table 3: Transfer function codes
#define HART3_TRANSFERFUNC_LINEAR 0

// Table 6: Alarm selection codes
#define HART6_ALARM_HIGH 0
#define HART6_ALARM_LOW  1

// Table 7: Write protect codes
#define HART7_WRITEPROTECT_NO        0
#define HART7_WRITEPROTECT_YES       1
#define HART7_WRITEPROTECT_NOTUSED 250
#define HART7_WRITEPROTECT_NONE    251
#define HART7_WRITEPROTECT_UNKNOWN 252
#define HART7_WRITEPROTECT_SPECIAL 253

// Table 9: Burst mode control codes
#define HART9_BURST_OFF              0
#define HART9_BURST_TOKEN            1
#define HART9_BURST_TDMA             2
#define HART9_BURST_TDMATOKEN        3
#define HART9_BURST_HARTIP           4

// Table 10: Physical signaling codes
#define HART10_SIGNALING_WIRELESS    4

// Table 11: Flag assignments
#define HART11_MULTISENSOR_FD        0x01
#define HART11_IEEE_WIRELESS         0x08

// Table 16: Loop current mode codes
#define HART16_LOOPSIGNALING_DIS     0
#define HART16_LOOPSIGNALING_EN      1

// Table 26: Analog Channel Flags
#define HART26_ANALOGCHANNEL_OUTPUT 0
#define HART26_ANALOGCHANNEL_INPUT  1

// Table 33: Burst Message Trigger Mode Codes
#define HART33_TRIGGERMODE_CONTINUOUS 0
#define HART33_TRIGGERMODE_WINDOW     1
#define HART33_TRIGGERMODE_RISING     2
#define HART33_TRIGGERMODE_FALLING    3
#define HART33_TRIGGERMODE_ONCHANGE   4

// Table 35: Event notification control codes
#define HART35_EVENTNOTIF_OFF              0
#define HART35_EVENTNOTIF_TOKEN            1
#define HART35_EVENTNOTIF_TDMA             2
#define HART35_EVENTNOTIF_TDMATOKEN        3

// Table 57: Device profile codes
#define HART57_PROFILE_WHART_DISCRETE 130

// Table 66: Squawk control codes
#define HART66_SQUAWK_OFF   0
#define HART66_SQUAWK_ON    1
#define HART66_SQUAWK_ONCE  2

//=========================== typedef =======================================

//==== local hart variables
typedef struct
{
    uint16_t                   expDevicetype;
    uint32_t                   uniqueID;       // LS 24bit
    bool                       initialized;
} hart_lib_vars_t;

//=========================== variables =======================================

// init flash data structure
static const hart_flash_memImage_t flashInitData __attribute__((at(HART_FLASH_ADDR))) =
{
    // Configuration counter is zeroed at program load
    // (this does not have to be here, C zeroes not initialized members of strcut)
    .cfgCounter.value          = 0,
    .cfgCounter.flags          = {0,0,0,0},

    // Burst 0 is enabled by default
    .burst[0].mode             = HART9_BURST_OFF,
    .burst[0].slot             = {
        HART34_VARCODE_PRIMARY,HART34_VARCODE_SECONDARY,HART34_VARCODE_TERTIARY,HART34_VARCODE_QUATERNARY,
        HART34_VARCODE_NOT_USED,HART34_VARCODE_NOT_USED,HART34_VARCODE_NOT_USED,HART34_VARCODE_NOT_USED
    },
    .burst[0].cmdNum           = 3,                             // HART cmd 3 will be used by default
    .burst[0].updatePeriod     = 320000,                        // 10 seconds
    .burst[0].maxUpdatePeriod  = 320000,                        // 10 seconds
    .burst[0].triggerMode      = HART33_TRIGGERMODE_CONTINUOUS, // continous data bursting
    .burst[0].triggerLevel     = 1,                             // not used
    .burst[0].triggerClass     = HART21_CLASS_NONE,             // not used
    .burst[0].triggerUnit      = HART2_UNIT_NOT_USED, 					// not used

    // Event 0 is disabled by default
    .event[0].mode             = HART35_EVENTNOTIF_OFF,
//		.event[0].status;
//		.event[0].statusMask;
//		.event[0].pendingEventTime;
//		.event[0].retryPeriod;
//		.event[0].maxUpdatePeriod;
//		.event[0].debouncePeriod;
    //.event[0].eventMask,

    .longTag          = "Analog Devices, WH-mote",
    .shortTag         = {0x04,0x42,0x6D,0x34,0xF5,0x05}, // "ADI-MOTE"
    .descriptor       = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, // packed char '?',
    .date             = {23, 6, 3},  // 23.6.1993
    .message          = {
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,  // packed char '?'
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
    }, // not initialized
    .pollingAddr      = 0,
    .finalAssemblyNum = 1
};

// pointer to nonvolatile data in flash
static const hart_flash_memImage_t *flashData = &flashInitData;

// local variables
hart_lib_vars_t hart_lib_vars;

//=========================== prototypes ======================================

//==== Read commands
int16_t hart_universalCmd0(uint8_t *out); // this command is called multiple times, so it is not inline
__STATIC_INLINE int16_t hart_universalCmd1(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd2(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd3(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd7(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd8(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd12(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd13(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd14(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd15(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd16(uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd20(uint8_t *out);

//==== Write commands
__STATIC_INLINE int16_t hart_universalCmd6(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd9(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd11(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd17(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd18(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd19(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd21(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd22(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_universalCmd38(const uint8_t *in, uint8_t inLen, uint8_t *out, hart_lib_master_t master);
__STATIC_INLINE int16_t hart_universalCmd48(const uint8_t *in, uint8_t inLen, uint8_t *out);

__STATIC_INLINE int16_t hart_commonCmd33(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd54(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd72(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd73(uint8_t *out);

//==== Burst control
__STATIC_INLINE int16_t hart_commonCmd103(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd104(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd105(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd107(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd108(const uint8_t *in, uint8_t inLen, uint8_t *out);
__STATIC_INLINE int16_t hart_commonCmd109(const uint8_t *in, uint8_t inLen, uint8_t *out);

//==== Events control
__STATIC_INLINE int16_t hart_commonCmd118(const uint8_t *in, uint8_t inLen, uint8_t *out);

//==== Non-public
__STATIC_INLINE int16_t hart_nonPublicCmd123(const uint8_t *in, uint8_t inLen, uint8_t *out);

//=========================== public ==========================================

// HART lib must be initialized with valid MAC address
uint8_t hart_lib_init(uint8_t *macAddr)
{
	  uint8_t pol = flashData->pollingAddr;
    hart_lib_vars.expDevicetype = (macAddr[3] << 8)  | (macAddr[4]);
    hart_lib_vars.uniqueID      = (macAddr[5] << 16) |(macAddr[6] << 8) | (macAddr[7]);
    hart_lib_vars.initialized   = true;

    // TODO uncomment this to save battery life
    //gpio_ledGreen_Off();

    return pol;
}

// Commands: 0-767, 1024-64511, 64766-65535
int16_t hart_lib_cmd(uint16_t cmdId, const uint8_t *in, uint8_t inLen, uint8_t *out, uint8_t outLen, hart_lib_master_t master)
{
    return (-1);
}

//==== device status codes, HCF_SPEC-099, Table 12
#define HART_STATUS_MALFUNCTION   0x80  // not implemented in this code
#define HART_STATUS_CFG_CHANGED   0x40
#define HART_STATUS_COLD_START    0x20  // Set on reset, cleared after first response
#define HART_STATUS_MORE_STATUS   0x10
#define HART_STATUS_LOOP_FIXED    0x08  // always fixed
#define HART_STATUS_LOOP_SATURED  0x04  // HartCurrent outside 4-20mA
#define HART_STATUS_NPV_OFFLIMITS 0x02  // 
#define HART_STATUS_PV_OFFLIMITS  0x01  // HartPercent outside 0-100%

uint8_t hart_lib_getStatus(hart_lib_master_t master)
{
    return 0;
}

//==== extended device status codes, HCF_SPEC-183 Common table 17
#define HART_EXT_STATUS_FUNCTION_CHCK     0x20
#define HART_EXT_STATUS_OUT_OF_SPEC       0x10
#define HART_EXT_STATUS_FAIL              0x08
#define HART_EXT_STATUS_CRITICAL_PWR_FAIL 0x04
#define HART_EXT_STATUS_DV_ALERT          0x02
#define HART_EXT_STATUS_MAINTENANCE_REQ   0x01

uint8_t hart_lib_getExtendedStatus(void)
{
    return 0;
}

void    hart_lib_incCfgChangeCnt(void)
{
    // this fucntion is called only when CONFIG_CHANGED notification from LTC5800 is received
    // write it to flash
    hart_flash_write(NULL, NULL, 0, true);
}

int8_t  hart_lib_burstEnable(uint8_t burstNum, hart_lib_wirelessBurst_cbt Cb, bool enable)
{
    return 0;
}

int8_t  hart_lib_burstGetPeriod(uint8_t burstNum, uint32_t* period_ms)
{
    return 0;
}

//==== this function returns are information for bursting

int16_t hart_lib_burst(uint8_t burstNum, hart_lib_wirelessPacket_t *out)
{
   return (-1);
}

//=========================== private ==========================================

