#ifndef LBOOT_H_
#define LBOOT_H_

#include "lboot_config.h"
#include "stdint.h"
#include "lboot_prt.h"

#ifdef _WIN32
    #define SLEEP_MS(ms) Sleep(ms)
#else
    #include <unistd.h>

    #ifdef HAVE_USLEEP
        #define SLEEP_MS(ms) do {usleep(ms*1000);} while(0)
    #else
        #define SLEEP_MS(ms) do { \
            struct timespec time; \
            time.tv_sec = ms/1000; \
            time.tv_nsec = (ms%1000)*1000000UL; \
            for ( ; nanosleep(&time, &time)!=0; ) {} \
        } while(0)
    #endif
#endif

/** коды ошибок программы загрузчика */
typedef enum {
    LBOOT_ERR_MIN_ID                  = -1,
    /** опция заданная в командной строке не поддерживается */
    LBOOT_ERR_INVALID_OPTION       = -1,
    /** недостаточное количество аргументов */
    LBOOT_ERR_INSUFFICIENT_ARGUMENTS  = -2,
    /** указан неверный интерфейс для передачи прошивки устройству */
    LBOOT_ERR_INVALID_INTERFACE       = -3,
    LBOOT_ERR_FIRMWARE_FILE_OPEN      = -4,
    LBOOT_ERR_SIGNATURE_FILE_OPEN     = -5,
    LBOOT_ERR_HASH_FILE_OPEN          = -6,
    LBOOT_ERR_MEMORY_ALLOC            = -7,
    LBOOT_ERR_ALLOC_RESOURCE          = -8,
    LBOOT_ERR_DEVICE_NOT_FOUND        = -9,
    LBOOT_ERR_IOCTRL_FAILED           = -10,
    LBOOT_ERR_INVALID_DEVICE          = -11,
    LBOOT_ERR_INVALID_SERIAL          = -12,
    LBOOT_ERR_INVALID_DEVMODE         = -13,
    /** загрузчик не поддерживает возможность загрузки, без запуска основной программы */
    LBOOT_ERR_UNSUP_FLAG_DONT_START   = -14,
    /** загрузчик не поддерживает возможность восстановления основной прошивки по команде */
    LBOOT_ERR_UNSUP_FLAG_RECOVERY     = -15,
    /** указан неверный фармат вывода в опции --output-fmt */
    LBOOT_ERR_INVALID_OUTPUT_FMT      = -16,
    /** загрузчик не поддерживает запись защищенного блока данных */
    LBOOT_ERR_UNSUP_WR_PROT_DATA      = -17,
    /** ошибка открытия файла с даннымы для записи в защищенный блок */
    LBOOT_ERR_PROT_DATA_FILE_OPEN     = -18,
    LBOOT_ERR_INVALID_APP_IFACE       = -19,
    LBOOT_ERR_APP_IFACE_NOT_SPECIFIED = -20,
} t_lboot_pc_errs;


/** коды ошибок блока разбора OpenPGP записи в устройстве */
typedef enum {
    LBOOT_ERR_LCRYPT_OFFSET                      = -10000,
    LCRYPT_ERR_PGP_UNSUF_DATA                    = -10000,
    LCRYPT_ERR_PGP_SIGN_FORMAT                   = -10001,  /**< неверный формат PGP файла */
    LCRYPT_ERR_PGP_SIGN_UNSUPPORT_CRYPT_ALG      = -10002,  /**< неподдерживаемый алгоритм шифрования для подписи */
    LCRYPT_ERR_PGP_SIGN_UNSUPPORT_HASH_ALG       = -10003  /**< неподдерживаемый алгоритм вычисления hash-функции */
} t_lcrypt_errs;




/************   информация о устройстве ***********************************/

#define LBOOT_DEVNAME_SIZE            32
#define LBOOT_SERIAL_SIZE             32
#define LBOOT_SOFTVER_SIZE            32
#define LBOOT_REVISION_SIZE           16
#define LBOOT_IMPLEMENTATION_SIZE     16
#define LBOOT_SPECINFO_SIZE           64


#define LBOOT_DEVMODE_BOOT                0x1
#define LBOOT_DEVMODE_APPL                0x2

struct st_lboot_devinfo {
    char devname[LBOOT_DEVNAME_SIZE]; //название устройства
    char serial[LBOOT_SERIAL_SIZE];   //серийный номер
    char soft_ver[LBOOT_SOFTVER_SIZE]; //версия софта
    char brd_revision[LBOOT_REVISION_SIZE]; //аппаратная версия
    char brd_impl[LBOOT_IMPLEMENTATION_SIZE];
    char spec_info[LBOOT_SPECINFO_SIZE];
} ;
typedef struct st_lboot_devinfo t_lboot_devinfo;


typedef int (*t_lboot_firm_block_read)(void *ctx, uint8_t *buf, uint32_t size, uint32_t *ret_size);
typedef void (*t_lboot_firm_block_done)(void *ctx, int err, uint32_t size);




typedef int (*t_lboot_intf_init)(void);
typedef int (*t_lboot_intf_cleanup)(void);
typedef int (*t_lboot_parse_option)(int opt);
typedef const char* (*t_lboot_get_opt_descr)(void);
typedef const char* (*t_lboot_get_err_str)(int err);
typedef int (*t_lboot_open)(const char* devname, const char* serial,
                          const void* params, t_lboot_devinfo* info, int* out);
typedef int (*t_lboot_get_devmode)(uint8_t* mode);
typedef int (*t_lboot_get_bootloader_version)(uint16_t* ver);
typedef int (*t_lboot_get_dev_flags)(uint32_t* flags);
typedef int (*t_lboot_get_features)(uint32_t* flags);
typedef int (*t_lboot_start_write)(t_lboot_startwr_info wr_info);
typedef int (*t_lboot_firm_transfer)(void *ctx, t_lboot_firm_block_read block_rd,
                                     t_lboot_firm_block_done block_done, uint32_t fw_size);
typedef int (*t_lboot_spec_cmd)(const t_lboot_speccmd_info* cmd_info, uint32_t size);
typedef int (*t_lboot_write_fw_block)(const uint8_t* buf, uint32_t size);
typedef int (*t_lboot_write_sign)(const uint8_t* sign, uint32_t size);
typedef int (*t_lboot_close)(void);
typedef int (*t_lboot_app_to_boot)(void);

/* стандартная функция передачи прошивки, поторая читает прошивку блоками размером wr_block_size,
 * и записывает ее через fw_block_write  */
int lboot_fw_transfer_std_block(void *ctx, t_lboot_firm_block_read block_rd,
                                t_lboot_firm_block_done block_done, uint32_t fw_size);


typedef  struct {
    const char* name;            /**< название интерфейса */
    const char* descr;           /**< описание интерфейса */
    t_lboot_intf_init init;
    t_lboot_intf_cleanup cleanup;
    /** функция для разбора специфичных опций интерфейса (может быть NULL)*/
    t_lboot_parse_option opt_parse;
    /** получение строки с описанием специфичных опций интерфейса */
    t_lboot_get_opt_descr opt_descr;
    /** получение строки, соответствующей коду ошибки, специфичной для интерфейса */
    t_lboot_get_err_str   get_err_str;
} t_gen_iface_params;


/* переметры интерфейса для перехода из приложения в загрузчик */
typedef struct {
    t_gen_iface_params gen;
    int (*app_to_boot)(int *out);
} t_app_iface;


/** параметры интерфейса и его callback-функции */
typedef struct {
    t_gen_iface_params gen;
    const t_app_iface *default_app_iface;

    /** максимальный размер блока, который можно записать за раз */
    uint32_t wr_block_size;
    /** установить связ с модулем и получить информацию о модуле */
    t_lboot_open            open;
    t_lboot_get_devmode     get_devmode;
    t_lboot_get_bootloader_version get_bootldr_ver;
    t_lboot_get_dev_flags   get_dev_flags;
    t_lboot_get_features    get_features;
    t_lboot_start_write     start_write;
    /* Функция передачи прошивки в устройство. Можно воспользоваться
     * lboot_fw_transfer_std_block, если передача идет блоками по вызову
     * программы, реализовав fw_block_write, или реализовать свою функцию */
    t_lboot_firm_transfer   fw_transfer;

    t_lboot_write_fw_block  fw_block_write;
    t_lboot_write_sign      write_sign;
    t_lboot_spec_cmd        speccmd;
    t_lboot_close           close;
} t_boot_iface;




#endif
