#pragma once

#include "CookConfig.h"
#include <map>
#include <string>
#include <vector>
#include <list>
#include "CookTaskInfoBase.h"

U_NS_BEGIN_CATERING

class CCookVariant;
class CCookMenuInl;
class CCookFunItem;

// 菜谱基类
class ICookMenuBase
{
public:
    /** \brief  新建菜谱对象
    *
    *
    */
    ICookMenuBase();
    virtual ~ICookMenuBase ();

    /** \brief  初始化，主要对bin扩展区的【变量表】、【产品表】、【事件触发表】、【应用函数】进行解析
    *
    * \param pBinData  bin文件的起始地址
    * \param iLen  bin文件总长度
    * \param params  动态参数，目前支持的动态参数有taste、quantity、level
    * \note 暂时不支持同时在不同线程中初始化该对象，最好能在一个线程中依次初始化该对象，除非加锁保护
    * \note 必须在使用其他接口之前，先调用该接口进行初始化
    *
    */
    bool Init(const unsigned char* pBinData, int iBinLen, 
        const std::map<std::string, std::string>& params = std::map<std::string, std::string>());

    /** \brief 相关资源清理，不直接在析构函数中调用是因为可能在释放资源的时候，
    *           虚拟机可能还没执行完当前事件，需要访问资源
    *
    * \return 无
    * \note 必须在析构之前进行调用，且不能在析构函数和OnEndCookMenu中调用
    *
    */
    void Cleanup();

    /** \brief  获取菜谱数据（xml文件格式的字节流，比如 糖醋菠萝骨.xml 文件的字节流）
    *
    * \param pRecipeDataLen[in/out]  菜谱数据的长度
    * \return 返回对应的字节流，0表示失败
    *
    */
    const std::vector<unsigned char>* GetRecipeData()const;

    /** \brief  传感器数据发生变化时，发送事件【虚拟机会向主机查询任务相关的变量的值】
    *
    * \return true表示成功，false表示失败
    * \note 该接口需在StartTask之后调用才会有用
    *
    */
    bool PostEvent();

    /** \brief  底层真实产品某个字段的值改变时，发送事件
    *
    * \param trueProductId 真实产品id，比如【10501】
    * \param productFieldName 产品字段名称，比如【确定按键标志】
    * \param pProductFieldValue 产品字段当前的值，比如【1】
    * \return true表示成功，false表示失败
    * \note 该接口需在StartTask之后调用才会有用
    *
    */
    bool PostEvent(long trueProductId, const std::string& productFieldName, const CCookVariant* pProductFieldValue);

    /** \brief  主机异常信息推送
    *
    * \param errMsg 异常信息文本
    * \return true表示成功，false表示失败
    *
    */
    bool PostException(const std::string& errMsg);

    /** \brief  开始任务，并进行真实产品和虚拟产品的绑定
    *
    * \param taskId 任务id
    * \param trueObjectMap 任务需绑定的真实产品信息，包括《设备地址， 产品名称》；比如《10501， “ecooker_3.5kw”》
    * \return true表示开启任务成功，false表示开启任务失败
    *
    */
    bool StartTask(const std::string& taskId, const std::map<long, std::string>& trueObjectMap);

    /** \brief  暂停任务
    *
    * \param taskId 任务id
    * \return true表示暂停任务成功，false表示暂停任务失败
    * \note 暂停当前任务关联的定时器，并丢弃后续接收到的事件
    *
    */
    bool PauseTask(const std::string& taskId);

    /** \brief  恢复任务
    *
    * \param taskId 任务id
    * \return true表示恢复任务成功，false表示恢复任务失败
    * \note 恢复当前任务关联的定时器，并继续处理后续接收到的事件
    *
    */
    bool RestoreTask(const std::string& taskId);

    /** \brief  终止任务
    *
    * \param taskId 任务id
    * \return true表示终止任务成功，false表示终止任务失败
    * \note 终止当前任务关联的定时器，并重置任务关联的变量
    *
    */
    bool TerminateTask(const std::string& taskId);
    
    /** \brief  对任务的步骤进行确认，当点击主机【确认】的时候调用，因为【确认】和任务相关，所以就没有直接通过PostEvent接口来触发
    *
    * \param taskId 任务id
    * \return true表示成功，false表示失败（任务id无效，任务依赖的产品不满足条件）
    *
    */
    bool ConfirmTask(const std::string& taskId);

    /** \brief  获取菜谱任务当前的步骤和总步骤数
    *
    * \param taskId 任务id
    * \param pTotleStep[in/out] 总的步骤数
    * \param pCurStep[in/out] 当前步骤
    * \return true表示成功，false表示失败
    *
    */
    bool GetTaskCurStep(const std::string& taskId, int* pTotleStep, int* pCurStep) const;


	/** \brief  获取任务信息
	* \return true表示成功,taskItem为返回任务信息，false表示失败
	*
	*/
	bool GetTaskInfo(std::list<task_item_t> &taskItem) const;

	/** \brief  跳过当前步，提前结束当前步的计时等待
	*
	* \param taskId 任务id
	* \return true表示成功，false表示失败
	*
	*/
	bool SkipCurrentStep(const std::string& taskId) const;

    /** \brief  菜谱任务完成
    *
    * \param taskId 任务id
    * \return true表示成功，false表示失败
    *
    */
    virtual bool OnEndTask(const std::string& taskId) = 0;

    /** \brief  获取真实产品某个字段的当前值
    *
    * \param taskId 任务id
    * \param trueProductId 真实产品id，比如【3.5kw电磁炉1的唯一标识】
    * \param productFieldName 产品字段名称，比如【中心温度】
    * \param pRetValue[in/out] 产品字段当前的值，比如【36.5】
    * \return true表示成功，false表示失败
    *
    */
    virtual bool GetVar(const std::string& taskId, long trueProductId, const std::string& productFieldName,
        CCookVariant* pRetValue) = 0;
    
    /** \brief  设置真实产品某个字段的值
    *
    * \param taskId 任务id
    * \param trueProductId 真实产品id，比如【3.5kw电磁炉1的唯一标识】
    * \param productFieldName 产品字段名称，比如【中心温度】
    * \param pRetValue[in/out] 产品字段当前的值，比如【36.5】
    * \return true表示成功，false表示失败
    * \note 暂时用不到
    *
    */
    virtual bool SetVar(const std::string& taskId, long trueProductId, const std::string& productFieldName, 
        const CCookVariant* pVarValue) = 0;
    
    /** \brief  应用函数调用
    *
    * \param taskId 任务id
    * \param iCmdId 命令码，用作应用并发执行函数用（随机生成，且菜谱内唯一）
    * \param iCurStep 当前步骤
    * \param iTotleStep 总步骤
    * \param funItemList 函数集
    * \return true表示成功，false表示失败
    * \note 因运行的是一个函数集，故仅支持返回值为void类型的函数
    *
    */
    virtual bool RunAppFun(const std::string& taskId, int iCmdId, int iCurStep, int iTotleStep, 
        const std::vector<CCookFunItem*>& funItemList) = 0;
	
    /** \brief  菜谱完成
    *
    * \param bSuccess 是否正常退出，
    * \return true表示成功，false表示失败
    * \note 应用不能直接在此接口中释放资源，需发送信号给菜谱管理器来释放该菜谱资源
    *
    */
    virtual bool OnEndCookMenu(bool bSuccess = true) = 0;

protected:
    // 为了内部数据测试，添加的一个测试接口，仅仅虚拟机内部用作模拟测试用，应用请勿调用
    const void* GetCookDataForTest()const;
	void GetTaskStepInfoForTest(const std::string& taskId, int iStepNo, int* pVisibleStep, int* pVisibleId) const;
private:
    // 禁用拷贝
    U_DISABLE_COPY(ICookMenuBase)

private:
    CCookMenuInl* m_pCookMenuInl;
};

U_NS_END_CATERING

