#ifndef LTR_MODULE_H_
#define LTR_MODULE_H_

#include "ltrapi.h"
#include "ltrd_protocol_defs.h"


/*================================================================================================*/
#define LTR_MODULE_RESET_TIMEOUT       (5000)
#define LTR_MODULE_STOP_TIMEOUT        (5000)
#define LTR_MODULE_CMD_SEND_TIMEOUT    (3000)
#define LTR_MODULE_CMD_RECV_TIMEOUT    (4000)

#define LTR_MODULE_HDR_SIZE_MAX       (64*1024)


#ifndef MIN
    #define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
    #define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

#ifndef ABS
    #define ABS(a) ((a) >= 0 ? (a) : -(a))
#endif

/** Создание слова команды из данных и кода команды */
#define LTR_MODULE_MAKE_CMD(cmd, data) (((DWORD)(cmd) &0xFFFF) | ((DWORD)((data)&0xFFFF) << 16))
/** Создание слова команды из 2-х байт данных и кода команды */
#define LTR_MODULE_MAKE_CMD_BYTES(cmd, byte_h, byte_l) LTR_MODULE_MAKE_CMD(cmd, \
    (((WORD)(byte_h)&0xFF) << 8) | ((byte_l)&0xFF))


#define LTR_MODULE_CMD_DATA_MSK         0xFFFF0000UL
#define LTR_MODULE_CMD_CODE_MSK         0x0000F0DFUL
#define LTR_MODULE_CMD_GET_CMDCODE(cmd) (cmd & LTR_MODULE_CMD_CODE_MSK)

/** Извлечение поля данных из команды со сдвигом */
#define LTR_MODULE_CMD_GET_DATA(cmd)    ((cmd >> 16) & 0xFFFF)


#define LTR_MODULE_WRD_CHECK_DATA(wrd)    ((((wrd) & LTR010_CMD_DATA_MASK) == LTR010DATA) ? LTR_OK : LTR_ERROR_PROCDATA_UNEXP_CMD)


/*================================================================================================*/
enum en_LTR_OpenModuleFlags {
    /* признак, что после STOP-RESET не нужен второй STOP */
    LTR_MOPEN_INFLAGS_DEF_SEC_STOP  = 0x00000001,
    LTR_MOPEN_INFLAGS_DONT_RESET    = 0x00000002,
    /* признак, что не надо проводить дальнейшую инициализацию модуля */
    LTR_MOPEN_OUTFLAGS_DONT_INIT    = 0x00010000
};

enum en_LTR_StopModuleFlags {
    LTR_MSTOP_FLAGS_CHECK_PARITY    = 0x00000001, /** признак, что нужно проверить четность ответа */
    LTR_MSTOP_FLAGS_SPEC_MASK       = 0x00000002 /** используется специальная маска для проверки, вместо стандартной */
};


/*================================================================================================*/
/***************************************************************************//**
   Вспомогательная функция для открытия соединения с модулем LTR.
    Проверяет параметры, устанавливает связь с модулем, посылает
    START-RESET-STOP (последний STOP может не посылаться при флаге
    LTR_MOPEN_INFLAGS_DEF_SEC_STOP) и проверяет ответ на RESET.
    @param[in] hnd        Описатель модуля
    @param[in] net_addr   IP-адерс машины с LTR-сервером
    @param[in] net_port   TCP-порт для соединения с сервером
    @param[in] crate_sn   Серийный номер крейта (если пустая строка или NULL -
                          открывается первый найденный крейт)
    @param[in] slot_num   Номер слота, в котором находится модуль
                          (от CC_MODULE1 до СС_MODULE16)
    @param[in] mid        ID модуля (для проверки правильности ответа на RESET)
    @param[in,out] flags  Набор флагов из #en_LTR_OpenModuleFlags.
                          На входе - флаги LTR_MOPEN_INFLAGS управляют поведением Open
                          На выходе - флаги LTR_MOPEN_OUTFLAGS указывают доп информацию
    @param[out] ack       Если не NULL, то в этой переменной возвращается ответ
                          на RESET
    @param[out] warn      Если не NULL, то в этой переменной возвращается код
                          предупреждения
    @return               Код ошибки (без предупреждений - предупреждения
                          возвращаются через warn)
 ******************************************************************************/
INT ltr_module_open(TLTR *hnd, DWORD ltrd_addr,
                   WORD ltrd_port, const CHAR *csn, INT slot_num,
                   WORD mid, DWORD* flags, DWORD* ack, INT* warn);


/** Заполнение стандартной команды LTR-модулю типа INSTR. В качестве 5-го бита
    (начиная с нулевого) вычисляется четность команды
    @param[in] cmd     Код команды
    @param[in] param   Данные команды
    @return Слово для передачи в крейт, соответствующее команде */
DWORD ltr_module_fill_cmd_parity(WORD cmd, WORD param);


#define LTR_MODULE_FILL_CMD_PARITY_BYTES(cmd, byte_h, byte_l) ltr_module_fill_cmd_parity((cmd), \
                                                    ((byte_h)<<8) | (byte_l))

/** Проверка четности в ответе на команду
    @param[in] cmd    Команда для проверки
    @return           0 - если четность совпала, 1 - при не совпадении */
INT ltr_module_check_parity(DWORD cmd);

/** Передача массива команд. Все команды должны быть переданы, иначе будет
    возвращена ошибка LTR_ERROR_SEND_INSUFFICIENT_DATA
    @param[in] ltr    Описатель канала связи с LTR Server'ом
    @param[in] data   Массив команд для передачи
    @param[in] size   Количество команд
    @return           Код ошибки */
INT ltr_module_send_cmd(TLTR *ltr, const DWORD *data, DWORD size);

/** Прием подтверждений на команды. Если быле приняты не все подтверждения -
    возвращает ошибку LTR_ERROR_NO_CMD_RESPONSE
    @param[in] ltr    Описатель канала связи с LTR Server'ом
    @param[out] data  Массив, в который будут сохранены принятые ответы
    @param[in] size   Количество принимаемых ответов
    @return           Код ошибки */
INT ltr_module_recv_cmd_resp(TLTR *ltr, DWORD *data, DWORD size);

INT ltr_module_recv_cmd_resp_tout(TLTR *ltr, DWORD *data, DWORD size, DWORD tout);


INT ltr_module_send_with_single_resp(TLTR *ltr, const DWORD *cmds, DWORD size, DWORD *resp);

#define ltr_module_send_with_echo_resps(ltr, cmds, size, resp) \
    ltr_module_send_with_echo_resps_tout(ltr, cmds, size, resp, LTR_MODULE_CMD_RECV_TIMEOUT)

INT ltr_module_send_with_echo_resps_tout(TLTR* ltr, const DWORD* cmds, DWORD size, DWORD *resp, DWORD tout);

INT ltr_module_send_single_with_echo(TLTR *ltr, DWORD cmd);


/** Функция для останова потока данных - передает команду stop_cmd
    и вычитыват данные до прихода ack_cmd или пока не выйдет по таймауту
    @param[in] ltr       Описатель канала связи с LTR Server'ом
    @param[in] stop_cmd  Код команды останова
    @param[in] ack_cmd   Код ожидаемой команды подтверждения останова
    @param[in] flags     Набор флагов из en_LTR_StopModuleFlags
    @param[in] msk       Маска, указывает какие биты принятого слова будут сравниваться
                         с ack_cmd, чтобы определить совпадение (если указан флаг
                         #LTR_MSTOP_FLAGS_SPEC_MASK)
    @param[in] tout      Таймаут на время передачи
    @param[out] ack      Если не NULL, в этом слове сохраняется ответ
    @return              Код ошибки  */
INT ltr_module_stop_tout(TLTR *ltr, const DWORD *stop_cmd, DWORD stop_cmd_cnt,
                         DWORD ack_cmd, DWORD flags, DWORD msk, DWORD tout, DWORD *ack);

/** Функция вычитыват данные до прихода ack_cmd или пока не выйдет по таймауту
    @param[in] ltr       Описатель канала связи с LTR Server'ом
    @param[in] ack_cmd   Код ожидаемой команды подтверждения останова
    @param[in] flags     Набор флагов из en_LTR_StopModuleFlags
    @param[in] msk       Маска, указывает какие биты принятого слова будут сравниваться
                         с ack_cmd, чтобы определить совпадение (если указан флаг
                         #LTR_MSTOP_FLAGS_SPEC_MASK)
    @param[in] tout      Таймаут на время ожидания ответа
    @param[out] ack      Если не NULL, в этом слове сохраняется ответ
    @return              Код ошибки  */
INT ltr_module_recv_fnd_ack(TLTR *ltr, DWORD ack_cmd, DWORD flags, DWORD msk, DWORD tout, DWORD *ack);



#define ltr_module_stop(ltr, stop_cmd, stop_cmd_cnt, ack_cmd, flags, msk, ack) \
    ltr_module_stop_tout(ltr, stop_cmd, stop_cmd_cnt, ack_cmd, flags, msk, LTR_MODULE_STOP_TIMEOUT, ack)


LTRAPIWIN_DllExport(INT) LTR_PrivateBufAlloc(TLTR* ltr, DWORD id, DWORD size, DWORD flags);
LTRAPIWIN_DllExport(LPVOID) LTR_PrivateBufGet(TLTR* ltr, DWORD id);
LTRAPIWIN_DllExport(INT) LTR_PrivateBufFree(TLTR* ltr, DWORD id);

#endif
