/** \file
*
* \brief   该文件仅仅用作餐饮主机项目的测试用，其他测试请放在unit-test中
* \date
*
*/

#if defined U_APP_TEST && !defined U_GOOGLE_UNIT_TEST && !defined U_APP_MANNUAL_TEST
#include <iostream>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <sstream>

#if defined U_PLATFORM_WIN32 && defined U_PLATFORM_WIN32_VS_VLD
#include <vld.h>
#endif
#ifdef U_PLATFORM_WIN32
#include <Windows.h>
#elif defined U_PLATFORM_LINUX
#include <unistd.h>
#include <errno.h>
#endif
#include "../include/CookMenuBase.h"
#include "../include/CookVariant.h"
#include "../include/CookLog.h"
#include "../include/CookFunItem.h"

using namespace ns_catering;

class CCookMenu;

static std::list<CCookMenu* > g_cookMenus;
static bool g_needQuitTask = false;
static bool g_needQuitReceipe = false;

bool WriteContentToFile(const char* pBinFilePath, const char* pFileContent, int iSize);
static std::string UTF8ToANSI(std::string s);

class cookengine;
class CCookMenu : public ICookMenuBase
{
public:
    CCookMenu(cookengine* engine)
        : ICookMenuBase()
        , m_bNeedQuit(false)
    {
    }
    ~CCookMenu(){}

    virtual bool OnEndTask(const std::string& taskId)
    {
        // 任务执行结束
        g_needQuitTask = true;
        return true;
    }

    virtual bool GetVar(const std::string& taskId, long trueProductId, const std::string& productFieldName, CCookVariant* pRetValue)
    {
#if 0
        std::cout << "GetVar, iTaskid= " << taskId << ", trueProductId= " << trueProductId << ", productFieldName=" << productFieldName << std::endl;

        pRetValue->SetValue(1);
        return true;
#else
        // 因设备的当前值不好模拟，就直接返回false了，主机需在获取到设备的值以后，返回true
        return false;
#endif
    }

    virtual bool SetVar(const std::string& taskId, long trueProductId, const std::string& productFieldName, const CCookVariant* pVarValue)
    {
        // 示例：设置电磁炉的中心温度为36.5度， trueProductId="ecooker_3.5kw1", productFieldName="SnrMainT", pVaVale=3000
        std::cout << "SetVar: iTaskId= " << taskId << ", trueProductId= " << trueProductId << ", productFieldName=" << productFieldName << std::endl;
        return true;
    }

    virtual bool RunAppFun(const std::string& taskId, int iCmdId, int iCurStep, int iTotleStep, const std::vector<CCookFunItem*>& funItemList)
    {
        // 
        int iTmpCurStep = 0;
        int iTmpTotleStep = 0;
        bool bRet = this->GetTaskCurStep(taskId, &iTmpCurStep, &iTmpTotleStep);

        // TODO: 应用需遍历funItemList
        std::cout << "RunAppFun: taskid: " << taskId << 
            ", iCmdId: " << iCmdId << 
            ", iCurStep: " << iCurStep << 
            ", iTotleStep: " << iTotleStep;
        for (unsigned int i = 0; i < funItemList.size(); ++i)
        {
            std::vector<unsigned char> vc = funItemList[i]->getFunParams()[0]->ToByteArray();
            std::string firstParam(reinterpret_cast<char const*>(vc.data()), vc.size());
            std::string funName = funItemList[i]->getFunName();
            std::cout << ", funName: " << funName << 
                ", trueProductId: " << funItemList[i]->getTrueProductId() <<
                ", funParamNum: " << funItemList[i]->getFunParams().size() << 
                ", funParam[0]: " << UTF8ToANSI(firstParam) << std::endl;
            const char* pParamTmp = firstParam.c_str();
        }
        return true;
    }

	virtual bool OnEndCookMenu(bool bSuccess)
    {
        g_needQuitReceipe = true;
        // 菜谱执行结束
        m_bNeedQuit = true;
        std::cout << "end receipe running....." << std::endl;
        return true;
    }

    bool NeedQuit()
    {
        //  仅仅用做测试，没有考虑安全性
        return m_bNeedQuit;
    }
private:
    bool m_bNeedQuit;
};

static std::string UTF8ToANSI(std::string s)
{
#ifdef U_PLATFORM_WIN32
    BSTR    bstrWide;
    char*   pszAnsi;
    int     nLength;
    const char *pszCode = s.c_str();

    nLength = MultiByteToWideChar(CP_UTF8, 0, pszCode, strlen(pszCode) + 1, NULL, NULL);
    bstrWide = SysAllocStringLen(NULL, nLength);

    MultiByteToWideChar(CP_UTF8, 0, pszCode, strlen(pszCode) + 1, bstrWide, nLength);

    nLength = WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, NULL, 0, NULL, NULL);
    pszAnsi = new char[nLength];

    WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, pszAnsi, nLength, NULL, NULL);
    SysFreeString(bstrWide);

    std::string r(pszAnsi);
    delete[] pszAnsi;
    return r;
#else
    return s;
#endif
}

bool WriteContentToFile(const char* pBinFilePath, const char* pFileContent, int iSize)
{
    FILE* pFile = 0;
    pFile = fopen((const char*)pBinFilePath, "wb");
    if (!pFile)
    {
        return false;
    }
    int iActualWriteSize = fwrite(pFileContent, 1, iSize, pFile);
    if (iSize != iActualWriteSize)
    {
        fclose(pFile);
        return false;
    }
    fclose(pFile);
    return true;
}

static char* ReadFile(const char* pBinFilePath, int* pSize)
{
    unsigned int iFileSize = 0;
    unsigned int iReadSize = 0;
    char* pBuf = 0;
    FILE* pFile = 0;
    pFile = fopen((const char*)pBinFilePath, "rb");

    if (!pFile)
    {
        perror("open error.");
        return 0;
    }
    fseek(pFile, 0, SEEK_END);
    iFileSize = ftell(pFile);
    fseek(pFile, 0, SEEK_SET);
    if (0 == iFileSize)
    {
        fclose(pFile);
        return 0;
    }
    pBuf = new char[iFileSize];// (char*)malloc(iFileSize);
    memset(pBuf, 0, iFileSize);
    iReadSize = fread(pBuf, 1, iFileSize, pFile);
    if (iReadSize != iFileSize)
    {
        fclose(pFile);
        delete[] pBuf;//free(pBuf);
        return 0;
    }

    *pSize = iFileSize;
    fclose(pFile);

    return pBuf;
}

static void TestSleep(int milliseconds)
{
#ifdef U_PLATFORM_WIN32
    Sleep(milliseconds);
#elif _POSIX_C_SOURCE >= 199309L
    struct timespec ts;
    ts.tv_sec = milliseconds / 1000;
    ts.tv_nsec = (milliseconds % 1000) * 1000000;
    nanosleep(&ts, 0);
#else
    usleep(milliseconds * 1000);
#endif
}

static void StartTask(std::string taskId, std::map<long, std::string>& trueObjectMap)
{
    for (std::list<CCookMenu* >::const_iterator iter = g_cookMenus.begin(); iter != g_cookMenus.end(); ++iter)
    {
        // 默认主机开启任务
        (*iter)->StartTask(taskId, trueObjectMap);
    }
}

static void ConfirmTask(const std::string& taskId)
{
    for (std::list<CCookMenu* >::const_iterator iter = g_cookMenus.begin(); iter != g_cookMenus.end(); ++iter)
    {
        // 模拟主机确认按键按下
        (*iter)->ConfirmTask(taskId);
    }
}

static void PostEventForSensor()
{
    for (std::list<CCookMenu* >::const_iterator iter = g_cookMenus.begin(); iter != g_cookMenus.end(); ++iter)
    {
        // 模拟传感器数据发生变化
        (*iter)->PostEvent();
    }
}

static void PostEventForInput(long trueProductId, const std::string& fieldName, CCookVariant val)
{
    for (std::list<CCookMenu* >::const_iterator iter = g_cookMenus.begin(); iter != g_cookMenus.end(); ++iter)
    {
        // 模拟交互性的事件
        (*iter)->PostEvent(trueProductId, fieldName, &val);
    }
}

static void WaitComplete()
{
    // 为了测试方便，用sleep轮询方式来销毁菜谱对象
    while (!g_cookMenus.empty())
    {
        std::list<CCookMenu* >::iterator iter = g_cookMenus.begin();
        while (iter != g_cookMenus.end())
        {
            // 判断菜谱对象是否执行完毕
            if ((*iter)->NeedQuit())
                // if (1)
            {
                (*iter)->Cleanup();
                delete *iter;
                iter = g_cookMenus.erase(iter);
            }
            else
            {
                // 为了方便，仅仅在主线程中发送传感器的值变化事件，没有单独开启线程
                PostEventForSensor();

                ++iter;
            }
        }
        TestSleep(200);
    }
}

static void CreateRecipeObjects()
{
    int iNum = 1;
    //std::cout << "pls input the receipe object num: ";
    // std::cin >> iNum;
    for (int i = 0; i < iNum; ++i)
    {
        // 读取bin文件
        int iBinLen = 0;
#ifdef U_PLATFORM_WIN32
        char* pBuf = ReadFile("../../../src/test/unit-tests/down.bin", &iBinLen);
#elif defined U_PLATFORM_LINUX
        char* pBuf = ReadFile("../../../src/test/unit-tests/down.bin", &iBinLen);
#endif
        if (NULL == pBuf)
        {
            std::cout << "read bin file error: " << strerror(errno) << std::endl;
            return;
        }
        // 创建菜谱对象
        CCookMenu* pCookeMenu = new CCookMenu(NULL);
        // 初始化菜谱对象
        std::map<std::string, std::string> params;
        //params["taste"] = "0;
        //params["quantity"] = "3";
        //params["level"] = "4";
        pCookeMenu->Init(reinterpret_cast<const unsigned char*>(pBuf), iBinLen, params);
#if 1
        // 获取xml文件的内容
        const std::vector<unsigned char>* pData = pCookeMenu->GetRecipeData();
        printf("data: %s\n", pData->data());
        std::string receipeData(reinterpret_cast<const char*>(pData->data()), pData->size());

        const unsigned char * pTrueData = pData->data();
        WriteContentToFile("receipeData.xml", (const char*)pTrueData, pData->size());
        // 需解析【应用扩展区-菜谱数据】，xml格式，原始菜谱数据，编译器仅仅去掉了step字段的值
#endif
        g_cookMenus.push_back(pCookeMenu);
        delete[] pBuf;
    }
}

static void CateringLog(const char* pStr)
{
    std::cout << UTF8ToANSI(pStr) << std::endl;
}

static void Run()
{
#if 1 // just test

    // 首先注册日志的输出回调
    COOKLOG.InitHook(CateringLog);
    

	//bin文件包括4部分，分别为【文件头】【虚拟机数据区】【应用扩展区-数据表】【应用扩展区-xml菜谱数据】
    //应用主要负责解析【应用扩展区-xml菜谱数据】
	int iBinLen = 0;
    char* pBuf = ReadFile("../../../src/test/unit-tests/down.bin", &iBinLen);

	// 1. 创建一个菜谱实例
	CCookMenu* pCookMenu = new CCookMenu(NULL);

	// 2. 初始化菜谱实例，出入菜谱bin文件和菜谱动态因子（份量、口味、档位）
	std::map<std::string, std::string> params;
	params["quantity"] = "1";//份量
	params["taste"] = "0";//口味, 0:蒜香
	params["level"] = "1";//档位, 0:一星, 1:二星, 2:三星
	pCookMenu->Init(reinterpret_cast<const unsigned char*>(pBuf), iBinLen, params);
    // 3. 开启第一个任务，绑定任务相关的产品实例
    std::map<long, std::string>trueObjectMap;
    trueObjectMap.insert(std::make_pair(2, "host"));
    trueObjectMap.insert(std::make_pair(1, "batcher_business_multi_export"));
    trueObjectMap.insert(std::make_pair(0, "bizcooker_stirfry_18kw"));
    StartTask("1", trueObjectMap);
    
	// 菜谱执行过程中需要等待bizcooker_stirfry_18kw确定按键按下
	// 4. 向菜谱发送事件，bizcooker_stirfry_18kw确定按键按下事件（示例）
    CCookVariant eventVal(1);
	pCookMenu->PostEvent(0, "Enter", &eventVal);

    // 5. 等待菜谱执行完毕，然后销毁实例(示例)
    Sleep(420000);
    pCookMenu->Cleanup();
    delete pCookMenu;

#else
    std::cout << "begin test receipe....." << std::endl;
    // 首先注册日志的输出回调
    COOKLOG.InitHook(CateringLog);

    // 创建菜谱对象集
    CreateRecipeObjects();
    // 开启第一个任务，绑定任务相关的产品
    std::map<long, std::string>trueObjectMap;
    trueObjectMap.insert(std::make_pair(10501, "ecooker_3.5kw"));
    trueObjectMap.insert(std::make_pair(10502, "batcher_slidtable"));
    // 不会触发，因为产品不够
    StartTask("0", trueObjectMap);
    // 真正的触发事件
    trueObjectMap.insert(std::make_pair(10500, "host"));
    StartTask("0", trueObjectMap);

#if 1
    CCookVariant eventVal(1);
    PostEventForInput(10501, "Enter", eventVal);
    TestSleep(12000);
    PostEventForInput(10501, "Enter", eventVal);
    //PostEventForInput(10500, "Confirm", eventVal);
    //TestSleep(10000);
    //PostEventForInput(10500, "Confirm", eventVal);
    //PostEventForInput(10500, "Confirm", eventVal);

#endif

    WaitComplete();
    std::cout << "end test receipe....." << std::endl;

#endif
}

void testOther()
{
    double dTest = -25.0;
    double dTest1 = 25.0;
    int i1 = -25;
    unsigned int i2 = (unsigned int)i1;
    int i6 = i2;

    unsigned int i3 = -25;
    unsigned int i4 = dTest;
    unsigned int i5 = (unsigned int)dTest;
    unsigned char c = static_cast<unsigned char>(dTest);
    double dTmp = -25;
    unsigned int iTmp = (unsigned int)dTmp;
    double dTmp2 = (double)iTmp;
    printf("convert double to byte: %d", c);
}

int main(int argc, char* argv[])
{
    testOther();
    //while (1)
    {
        Run();
    }
    return 0;
}


#endif
