#include "ltr01api.h"
#include "ltrmodule.h"
#include "lbitfield.h"
#include <string.h>
#include <stdio.h>

#define CMD_ID_REQUEST              (LTR010CMD_INSTR | 0x00)
#define RESP_ID_REQUEST             (LTR010CMD_INSTR | 0x00)

#define RESP_ID_FLD_SUBID_L         (0x0FFFUL <<  0U)
#define RESP_ID_FLD_SUBID_H         (0x0001UL << 15U)
#define RESP_ID_FLD_MODIFICATION    (0x0007UL << 12U)

#define LTR01_SUBID_FLD_NUM         (0x03FFUL <<  0U)
#define LTR01_SUBID_FLD_GROUP       (0x0007UL << 10U)

static struct {
    WORD id;
    const char *name;
} f_modules_names[] = {
    {LTR01_SUBID_LTRS511, "LTRS511"},
    {LTR01_SUBID_LTRS411, "LTRS411"},
    {LTR01_SUBID_LTRS412, "LTRS412"},
    {LTR01_SUBID_LTRT10,  "LTRT10"},
    {LTR01_SUBID_LTRT13,  "LTRT13"},
    {LTR01_SUBID_LTRK511, "LTRK511"},
    {LTR01_SUBID_LTRK416, "LTRK416"},
    {LTR01_SUBID_LTRK415, "LTRK415"},
    {LTR01_SUBID_LTRK71,  "LTRK71"},
};



LTR01API_DllExport(INT) LTR01_Open(TLTR *hnd, DWORD ltrd_addr, WORD ltrd_port,
                                   const CHAR *csn, INT slot_num, WORD subid,
                                   BYTE *cpld_ver, BYTE *modification) {
    DWORD open_flags = 0;
    INT warning, err = hnd==NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (err == LTR_OK) {
        DWORD ack;
        err = ltr_module_open(hnd, ltrd_addr, ltrd_port, csn, slot_num,
                              LTR_MID_LTR01, &open_flags, &ack, &warning);

        /* проверяем совпадение дополнительного идентификатора, если нужно */
        if ((err == LTR_OK) && (subid != LTR01_SUBID_INVALID)) {
            WORD recv_subid;
            err = LTR01_GetModuleSubID(hnd, &recv_subid, modification);
            if ((err == LTR_OK) && (recv_subid != subid))
                err = LTR_ERROR_INVALID_MODULE_ID;
        }

        /* возвращаем версию CPLD, если нужно */
        if ((err == LTR_OK) && (cpld_ver != NULL)) {
            *cpld_ver = ack & 0x3F;
        }
    }
    return err==LTR_OK ? warning : err;
}


LTR01API_DllExport(INT) LTR01_Close(TLTR *hnd) {
    return LTR_Close(hnd);
}

LTR01API_DllExport(INT) LTR01_GetModuleSubID(TLTR *hnd, WORD *subid, BYTE *modification) {
    INT err = subid == NULL ? LTR_ERROR_PARAMETERS : LTR_IsOpened(hnd);
    if (err == LTR_OK) {
        DWORD cmd = LTR_MODULE_MAKE_CMD(CMD_ID_REQUEST, 0);
        DWORD resp = LTR_MODULE_MAKE_CMD(RESP_ID_REQUEST, 0);
        err = ltr_module_send_with_single_resp(hnd, &cmd, 1, &resp);
        if (err == LTR_OK) {
            WORD data = LTR_MODULE_CMD_GET_DATA(resp);
            *subid = LBITFIELD_GET(data, RESP_ID_FLD_SUBID_L) | (LBITFIELD_GET(data, RESP_ID_FLD_SUBID_H) << 12U);
            if (modification != NULL) {
                *modification = LBITFIELD_GET(data, RESP_ID_FLD_MODIFICATION);
            }
        }
    }
    return err;
}

LTR01API_DllExport(INT) LTR01_GetSubID(DWORD ltrd_addr, WORD ltrd_port,
                                       const CHAR *csn, INT slot_num,
                                       WORD *subid, BYTE *modification) {
    TLTR hnd;
    INT err;
    LTR_Init(&hnd);
    err = LTR01_Open(&hnd, ltrd_addr, ltrd_port, csn, slot_num, LTR01_SUBID_INVALID, NULL, NULL);
    if (err == LTR_OK) {
        err = LTR01_GetModuleSubID(&hnd, subid, modification);
    }

    if (LTR_IsOpened(&hnd) == LTR_OK) {
        INT close_err = LTR01_Close(&hnd);
        if (err == LTR_OK)
            err = close_err;
    }
    return err;
}

LTR01API_DllExport(LPCSTR) LTR01_GetModuleName(WORD subid) {
    const char *res = "LTR01 (Unknown)";
    unsigned i, fnd;
    for (i = 0, fnd = 0; !fnd && (i < sizeof(f_modules_names)/sizeof(f_modules_names[0])); ++i) {
        if (f_modules_names[i].id == subid) {
            fnd = 1;
            res = f_modules_names[i].name;
        }
    }
    return res;
}

LTR01API_DllExport(INT) LTR01_GetModuleNameEx(WORD subid, BYTE modification, CHAR *name) {
    INT err = name != NULL ? LTR_OK : LTR_ERROR_PARAMETERS;
    if (err == LTR_OK) {
        const BYTE group = LBITFIELD_GET(subid, LTR01_SUBID_FLD_GROUP);
        const BYTE num = LBITFIELD_GET(subid, LTR01_SUBID_FLD_NUM);
        if (group == LTR01_SUBID_GROUP_GENERAL) {
            unsigned i, fnd;
            for (i = 0, fnd = 0; !fnd && (i < sizeof(f_modules_names)/sizeof(f_modules_names[0])); ++i) {
                if (f_modules_names[i].id == subid) {
                    fnd = 1;
                    if (modification == 0) {
                        strcpy(name, f_modules_names[i].name);
                    } else {
                        sprintf(name, "%s-%d", f_modules_names[i].name, modification);
                    }
                }
            }
            if (!fnd) {
                err = LTR_ERROR_UNKNOWN_MODULE_ID;
            }
        } else if (group == LTR01_SUBID_GROUP_LTRK) {
            if (modification == 0) {
                sprintf(name, "LTRK%d", modification);
            } else {
                sprintf(name, "LTRK%d-%d", num, modification);
            }
        } else {
            err = LTR_ERROR_UNKNOWN_MODULE_ID;
        }
    }
    return err;

}

