#include "ltrs412api.h"
#include "ltrmodule.h"
#include "lbitfield.h"
#include <stdlib.h>
#include <string.h>
#include "crc.h"
#include "ltrmodule_at93c86a.h"
#include "ltrmodule_flash_geninfo.h"


#if (LTRS412_SERIAL_SIZE != LTR_FLASH_GENINFO_SERIAL_EX_SIZE) || (LTRS412_NAME_SIZE != LTR_FLASH_GENINFO_NAME_EX_SIZE)
    #error module info len mismatch
#endif


#define CMD_SET_RELAY_STATES            (LTR010CMD_INSTR | 0x01)


#define LTRS412_FLASH_INFO_FORMAT         1
#define LTRS412_FLASH_INFO_ADDR           0
#define LTRS412_FLASH_INFO_SIZE_MIN       sizeof(t_ltrs412_flash_info)
#define LTRS412_FLASH_INFO_SIZE_MAX       1024

LTRS412API_DllExport(INT) LTRS412_ReadInfo(TLTRS412 *hnd);

typedef struct {
    t_ltr_flash_geninfo_hdr hdr;
    t_ltr_flash_geninfo_module_ex module;
} t_ltrs412_flash_info;

static void f_info_init(TLTRS412 *hnd) {
    memset(&hnd->ModuleInfo, 0, sizeof(hnd->ModuleInfo));
    strcpy(hnd->ModuleInfo.Name, "LTRS412");
    memset(&hnd->State, 0, sizeof(hnd->State));
}

LTRS412API_DllExport(INT) LTRS412_Init(TLTRS412 *hnd) {
    INT err = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (err == LTR_OK){
        memset(hnd,0, sizeof(TLTRS412));
        hnd->size = sizeof(TLTRS412);
        f_info_init(hnd);
        err=LTR_Init(&hnd->Channel);
    }
    return err;
}


LTRS412API_DllExport(INT) LTRS412_Open(TLTRS412 *hnd, DWORD net_addr, WORD net_port,
                                       const CHAR *csn, WORD slot) {
    INT err = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    INT rdcfg_err = LTR_OK;
    BYTE pld_ver;
    if (err == LTR_OK) {
        err = LTR01_Open(&hnd->Channel, net_addr, net_port, csn, slot,
                         LTR01_SUBID_LTRS412, &pld_ver, NULL);
    }

    if (err == LTR_OK) {
        hnd->ModuleInfo.VerPLD = pld_ver;
        f_info_init(hnd);
        rdcfg_err = LTRS412_ReadInfo(hnd);
    }

    /* по ошибкам read config не закрываем соединение */
    if (err != LTR_OK)
        LTRS412_Close(hnd);


    return err == LTR_OK ? rdcfg_err : err;
}


LTRS412API_DllExport(INT) LTRS412_Close(TLTRS412 *hnd) {
    INT err = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (err == LTR_OK) {
        err = LTR01_Close(&hnd->Channel);
    }
    return err;
}

LTRS412API_DllExport(INT) LTRS412_IsOpened(TLTRS412 *hnd) {
    return hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_IsOpened(&hnd->Channel);
}


LTRS412API_DllExport(INT) LTRS412_SetRelayStates(TLTRS412 *hnd, DWORD states) {
    INT err = LTRS412_IsOpened(hnd);
    if (err == LTR_OK) {
        DWORD cmd = LTR_MODULE_MAKE_CMD(CMD_SET_RELAY_STATES, states & 0xFFFF);
        DWORD ack;

        err = ltr_module_send_with_echo_resps(&hnd->Channel, &cmd, 1, &ack);
        if (err == LTR_OK) {
            hnd->State.RelayStates = states & 0xFFFF;
        }
    }
    return err;
}

LTRS412API_DllExport(INT) LTRS412_ReadInfo(TLTRS412 *hnd) {
    INT err = LTRS412_IsOpened(hnd);
    if (err == LTR_OK) {
        t_ltr_flash_geninfo_hdr hdr;
        err = ltr_at93c86a_read(&hnd->Channel, LTRS412_FLASH_INFO_ADDR, (BYTE*)&hdr, sizeof(hdr));
        if ((err == LTR_OK) && (hdr.sign != LTR_FLASH_GENINFO_SIGN))
            err = LTR_ERROR_FLASH_INFO_NOT_PRESENT;
        if ((err == LTR_OK) && (hdr.format != LTRS412_FLASH_INFO_FORMAT))
            err = LTR_ERROR_FLASH_INFO_UNSUP_FORMAT;
        if ((err == LTR_OK) && ((hdr.size < LTRS412_FLASH_INFO_SIZE_MIN) || (hdr.size > LTRS412_FLASH_INFO_SIZE_MAX)))
            err = LTR_ERROR_FLASH_INFO_UNSUP_FORMAT;
        if (err == LTR_OK) {
            t_ltrs412_flash_info *pinfo = malloc(hdr.size + LTR_FLASH_GENINFO_CRC_SIZE);
            if (pinfo == NULL)
                err = LTR_ERROR_MEMORY_ALLOC;
            /* читаем оставшуюся информацию */
            if (err == LTR_OK) {
                memcpy(pinfo, &hdr, sizeof(hdr));
                err = ltr_at93c86a_read(&hnd->Channel,
                                LTRS412_FLASH_INFO_ADDR+sizeof(hdr), ((BYTE *)pinfo) + sizeof(hdr),
                    (WORD)(hdr.size + LTR_FLASH_GENINFO_CRC_SIZE-sizeof(hdr)));
            }

            /* рассчитываем CRC и сверяем со считанной из памяти */
            if (err == LTR_OK) {
                WORD crc, crc_ver;
                crc_ver = eval_crc16(0, (unsigned char *)pinfo, hdr.size);
                crc = (WORD)(((BYTE *)pinfo)[hdr.size] | (((WORD)((BYTE *)pinfo)[hdr.size+1]) << 8));
                if (crc != crc_ver)
                    err = LTR_ERROR_FLASH_INFO_CRC;
            }


            if (err == LTR_OK) {
                memcpy(hnd->ModuleInfo.Name, pinfo->module.name, LTRS412_NAME_SIZE);
                memcpy(hnd->ModuleInfo.Serial, pinfo->module.serial, LTRS412_SERIAL_SIZE);
            }
            free(pinfo);
        }
    }
    return err;
}


LTRS412API_DllExport(INT) LTRS412_WriteInfo(TLTRS412 *hnd) {
    INT err = LTRS412_IsOpened(hnd);
    if (err == LTR_OK) {
        struct {
            t_ltrs412_flash_info info;
            WORD crc;
        } wrinfo, rdinfo;
        const size_t wr_size = sizeof(t_ltrs412_flash_info) + LTR_FLASH_GENINFO_CRC_SIZE;

        wrinfo.info.hdr.sign = LTR_FLASH_GENINFO_SIGN;
        wrinfo.info.hdr.format = LTRS412_FLASH_INFO_FORMAT;
        wrinfo.info.hdr.flags = 0;
        wrinfo.info.hdr.size = sizeof(t_ltrs412_flash_info);

        memcpy(wrinfo.info.module.name,   hnd->ModuleInfo.Name,   LTRS412_NAME_SIZE);
        memcpy(wrinfo.info.module.serial, hnd->ModuleInfo.Serial, LTRS412_SERIAL_SIZE);

        wrinfo.crc = eval_crc16(0, (BYTE *)&wrinfo.info, sizeof(t_ltrs412_flash_info));

        err = ltr_at93c86a_write(&hnd->Channel, LTRS412_FLASH_INFO_ADDR, (const BYTE*)&wrinfo, (WORD)wr_size);
        if (err == LTR_OK) {
            err = ltr_at93c86a_read(&hnd->Channel, LTRS412_FLASH_INFO_ADDR, (BYTE*)&rdinfo, (WORD)wr_size);
        }

        if ((err == LTR_OK) && memcmp(&wrinfo, &rdinfo, wr_size)) {
            err = LTR_ERROR_FLASH_VERIFY;
        }
    }
    return err;
}


LTRS412API_DllExport(LPCSTR) LTRS412_GetErrorString(INT err) {
    return LTR_GetErrorString(err);
}



