
#ifndef __PLXC_IVT2XX_API_H__
#define __PLXC_IVT2XX_API_H__

typedef enum EventCode
{
    ZengJiaHuanXingCi,
    ShanChuHuanXingCi,
    ZengJiaMingLingCi,
    ShanChuMingLingCi,
    XiaoFeiXiaoFei,

    WAKE_UP_XiaoFeiXiaoFei = 0x8000,
    WAKE_UP_Add,

    CNN_SLEEP         = 0xFEFE,                  //! 唤醒词触发后，长时间未接收到命令词，进入IDLE模式
    ALGO_TIME_OUT     = 0xFFFE,                  //!
    UN_RECOGNIZE      = 0xFFFF,                  //! 未识别
} eventCode_e;

/*!
 * 识别模式
 * */
typedef enum identifyMode
{
    COMMON_MODE,                                        //! 通用模式，优先识别唤醒词，唤醒词识别之后进入命令词识别模式
    COMMAND_MODE,                                       //! 只识别命令词模式
    WAKEUP_MODE,                                        //! 只识别唤醒词模式
    COMMAND_TURN,                                       //! 跳过唤醒词识别，直接进入命令词识别模式，识别周期到了之后，进入唤醒词识别
    COMMAND_END,                                        //! 立即结束命令词识别周期，进入到唤醒词识别
    EOF_MODE,                                           //! 模式结束符
} identifyMethod_e;


/**!
* 识别算法中的模型类型，分为唤醒词和命令词
*/
typedef enum modelType
{
    WAKEUP         = 0x8000,                          //! 唤醒词模式
    COMMAND        = 0,                               //! 命令词模式
    Unknow         = 0xFFFF,                          //! 未知模式
} modelType_e;

/**!
* 功耗枚举
*/
typedef enum powerType
{
    POWER_HIGH    = 0UL,                                //! 此时算法库要求mcu全速运行(算法库正在识别大模型)
    POWER_LOW     = 1UL,                                //! 此时算法库要求mcu可低速运行(算法库正在识别小模型)
    POWER_MINI    = 2UL,                                //! 迷你功耗，此时算法库正在空闲状态
} powerType_e;

/*
 * API 异常状态字
 */
typedef enum errorCode
{
    Success,                                          //! 执行成功
    Error1,                                           //! 参数异常
    Error2,                                           //! 条件不足
} errorCodeApi_e;

/*
 * 测试状态汇总
 * */
typedef enum algoTestMode
{
    Mode_Common,                                      //! 通用模式。即正常工作模式
    Mode_SoundRecord,                                 //! 录音模式，此处获取的是特征值，并非实际音频，音频有另外一套方案
    Mode_Replay,                                      //! 将录制好的音频，再次放到芯片中,需要配合一定的python脚本/软件
} algoTestMode_e;

/*
 * k-shot 算法状态
 */
typedef enum kshotState
{
    KSHOT_IDLE = 0,
    KSHOT_BUSY,
} kshotState_e;

/*
 * k-shot 录音结果
 */
typedef enum kshotRes
{
    KSHOT_NONE = 0,
    KSHOT_VERIFY_SUCCESS,
    KSHOT_VERIFY_FAILURE,
    KSHOT_VERIFY_NOISE,
    KSHOT_ADD_SUCCESS,
    KSHOT_ADD_FAILURE,
    KSHOT_ADD_NOISE,
} kshotRes_e;

#if 1
    /***************************PLXC API Begin**********************************/
    /*!
    * brief init mcu
    * */
    void     plxc_algo_init(void);

    /*!
    * brief Start and run the  mcu state machine
    * */
    void     plxc_algo_fsm(void);

    /*!
    * brief Start and run the  mcu state machine
    * */
    void     plxc_algo_thread_fsm(uint32_t);
    /**!
    * \brief 事件注册函数，通常用在识别算法上，在识别算法识别到关键词之后，通过回调形参中的函数通知应用端
    * \param 回调函数
    */
    void     plxc_event_notify_register(void(*mcuNotify)(eventCode_e keyword));

    /**!
    * \brief 将算法挂起，挂起后算法会在最近一次空闲时，通过参数中的函数回调通知应用，算法已完全处于空闲状态
    * \param mcuNotify : 算法通知应用回调
    */
    void     plxc_suspend_algo(void(*mcuNotify)(void));

    /**!
    * \brief 算法挂起后，重启算法
    */
    void     plxc_restart_algo(void);

    /*!
    * brief 命令词的冷却时间结束后，等待命令词的识别时长，若即在规定的时间内未识别到命令词，则重新计入唤醒词识别流程
    * */
    void     plxc_set_waittime_after_trigger(uint32_t time);

    /**!
    * \brief 设置模型在flash中的起始地址
    * \param addr ： 需要设置的模型地址
    * */
    void     plxc_set_model_addr(uint32_t addr);

    /**!
    * \brief 删除部分命令词(必须为同一类词，同为唤醒词或者同为命令词，不能混合)
    * \param keyBuff : 需要删除的命令词数组，每个index代表一个key
    * \param keyLen  : 需要删除的命令词的长度
    * */
    errorCodeApi_e plxc_delete_keywords(uint16_t *keyBuff, uint8_t keyLen);

    /**!
    * \brief 增加部分命令词(必须为同一类词，同为唤醒词或者同为命令词，不能混合)
    * \param keyBuff : 需要增加的命令词数组，每个index代表一个key
    * \param keyLen  : 需要增加的命令词的长度
    * */
    errorCodeApi_e plxc_add_keywords(uint16_t *keyBuff, uint8_t keyLen);

    /**!
    * \brief 设置命令词，注意：设置后只识别设置后的词(必须为同一类词，同为唤醒词或者同为命令词，不能混合)
    * \param keyBuff : 需要设置的命令词数组，每个index代表一个key
    * \param keyLen  : 需要设置的命令词的长度
    * */
    errorCodeApi_e plxc_set_keywords(uint16_t *keyBuff, uint16_t keyLen);

    /**!
    * \brief 重置识别词组，重置之后，所有支持的词都可被识别
    * \param type    : 需要重置的命令词类型,唤醒词/命令词
    * */
    void     plxc_reset_keywords(modelType_e type);

    /**!
    * \brief 返回算法使用的内存空间
    * \param  uint32_t : 长度地址，在此API中会对其赋值
    * \return uint32_t: 返回的地址空间的起始地址
    * */
    uint32_t plxc_get_algo_ram(uint32_t *len);

    /**!
    * @brief 设置识别模式 命令词模式/唤醒词模式/通用模式
    * 唤醒词模式  : 只识别唤醒词
    * 命令词模式  : 只识别命令词
    * 通用模式    : 先识别唤醒词,唤醒词识别后再识别命令词
    * \param identifyMethod_e: 需要设置的模式
    * */
    void     plxc_set_identify_mode(identifyMethod_e type);

    /**
    * @brief 确认当前是否已经配置此模式，若是此模式则返回1
    *        此种用法用于希望通过串口控制的情况下可用，外部通过串口配置，内部通过此api判断
    *        详情请参考At指令手册
    * @param mode : 确认的模式
    * @return :   1: 已经设置     0:未被设置
    */
    uint8_t  plxc_get_user_mode(uint8_t mode);

    /**!
    * \brief 算法at指令队列的tick函数，一般放在系统的system tick下,跟随system tick一起震动
    * **/
    void     plxc_algo_at_tick(void);

    /**!
    * \brief 串口数据输入，一次输入一个字节
    * \param data : 当前的输入字节
    * **/
    void     plxc_algo_at_in(uint8_t data);

    /**!
    * \brief  通知算法库，此项目可能会用上低功耗
    * \brief  算法库会在状态切换时，通知应用
    * \param lowPowerNotify : 低功耗回调函数
    */
    void     plxc_lowerPower_notify(void(*lowPowerNotify)(powerType_e type));

    /**!
    * \brief 设置获取audio的回调函数
    * \param getAudio ： 获取audio的回调函数指针
    * */
    void plxc_set_audio_callback(void (*getAudio)(int16_t *audio0, int16_t *audio1, uint32_t num_samples));

    /**!
    * \brief 设置擦Flash函数
    * \param eraseFlash ： 擦Flash函数指针
    * */
    void plxc_set_erase_flash(void (*eraseFlash)(void *addr, uint32_t len));

    /**!
    * \brief 设置读Flash函数
    * \param readFlash ： 读Flash函数指针
    * */
    void plxc_set_read_flash(uint32_t (*readFlash)(void *addr, void *buff, uint32_t len));

    /**!
    * \brief 设置写Flash函数
    * \param writeFlash ： 写Flash函数指针
    * */
    void plxc_set_write_flash(uint32_t (*writeFlash)(void *addr, void *buff, uint32_t len));

    /**!
    * \brief 注册k-shot算法回调函数，算法通过回调函数传递录音结果
    * \param 回调函数
    */
    void plxc_kshot_notify_register(void (*kshotNotify)(kshotRes_e res));

    /**!
    * \brief k-shot算法初始化，给k-shot算法分配一段4K对齐的Flash空间
    *        Flash空间4K对齐，len >= (num + 2) * 4KB
    * \param addr ： 分配给k-shot算法Flash空间的地址
    * \param len ： 分配给k-shot算法Flash空间的长度
    * \param num ： k-shot算法支持最大指令词条数，不超过6
    * \param sec ： k-shot算法录音时长，单位为秒，不超过10
    * */
    errorCodeApi_e plxc_kshot_init(void *addr, uint32_t len, uint32_t num, uint32_t sec);

    /**!
    * \brief k-shot算法增加一个指令词，若该指令词已增加过则覆盖更新
    * \param success_num ：录音成功次数，不超过5
    * \param max_num ：最大录音次数
    * \param code ： 指令词ID
    * */
    errorCodeApi_e plxc_kshot_add_keyword(eventCode_e code, uint32_t success_num, uint32_t max_num);

    /**!
    * \brief k-shot算法删除一个指令词
    * \param code ： 指令词ID
    * */
    errorCodeApi_e plxc_kshot_del_keyword(eventCode_e code);

    /**!
    * \brief k-shot算法查询一个指令词是否已被添加
    * \param code ： 指令词ID
    * */
    eventCode_e plxc_kshot_query_keyword(eventCode_e code);

    /**!
    * \brief k-shot算法设置阈值
    * \param recognition ： 识别阈值
    * \param verification ： 验证阈值
    * */
    void plxc_kshot_set_threshold(uint32_t recognition, uint32_t verification);

    /**!
    * \brief 获取k-shot算法状态
    * */
    kshotState_e plxc_kshot_get_state(void);

    /**
    * @brief 设置kaldi识别阈值
    *
    * @param softmax : 需要设置的阈值
    */
    void plxc_set_kaldi_threshold(modelType_e type, uint32_t selfloop, uint32_t trans);

    /**
    * @brief 设置降噪等级
    *
    * @param level : 降噪等级[0, 4]
    *            0 : 不降噪
    *            4 : 完全降噪
    */
    void plxc_set_denoise_level(uint32_t level);

    /**
    * @brief 设置应用层的at指令
    *
    * @param tLongcmd :  长指令 对应结构体(at_long_command_t *)
    * @param tShortCmd : 短指令 对应结构体(at_short_command_t *)
    */
    void plxc_set_at(uint32_t tLongcmd, uint32_t tShortCmd);


    /**!
    * \brief  注册aec回调
    * \param lowPowerNotify : 低功耗回调函数
    */
    void plxc_aec_notify(void(*aecNotify)(uint32_t, uint32_t));
    void plxc_aecnet_notify(void(*aecNotify)(uint32_t, uint32_t));
    /**
    * @brief aec应用关闭
    *
    * @param
    * @param
    */
    void plxc_close_aec();

    /*!
    * \brief 音频推送
    */
    void plxc_algo_audio_Push(uint32_t *audio, uint16_t len);

#else
    #define  plxc_algo_init()
    #define  plxc_algo_fsm()
    #define  plxc_event_notify_register(v1)
    #define  plxc_suspend_algo(v)
    #define  plxc_restart_algo(v)
    #define  plxc_set_waittime_after_trigger(v)
    #define  plxc_set_model_addr(v)
    #define  plxc_delete_keywords(v1,v2)                                (Success)
    #define  plxc_add_keywords(v1,v2)                                   (Success)
    #define  plxc_set_keywords(v1,v2)                                   (Success)
    #define  plxc_reset_keywords(v)
    #define  plxc_get_algo_ram(v)                                       (0)
    #define  plxc_set_identify_mode(v)
    #define  plxc_get_user_mode(v)                                      (0)
    #define  plxc_algo_at_tick()
    #define  plxc_algo_at_in(v)
    #define  plxc_lowerPower_notify(v)
    #define  plxc_set_erase_flash(v)
    #define  plxc_set_read_flash(v)
    #define  plxc_set_write_flash(v)
    #define  plxc_kshot_notify_register(v)
    #define  plxc_kshot_init(v1,v2,v3,v4)                                (Success)
    #define  plxc_kshot_add_keyword(v)                                   (Success)
    #define  plxc_kshot_del_keyword(v)                                   (Success)
    #define  plxc_kshot_query_keyword(v)                                 (UN_RECOGNIZE)
    #define  plxc_set_softmax(v1,v2,v3)
    #define  plxc_set_at(v1,v2)
    #define  plxc_aec_notify(v1)
    #define  plxc_aecnet_notify(v1)
    #define  plxc_aecnet_notify(v1)
    #define  plxc_close_aec()
#endif
#endif
