#pragma once

#include <memory>
#include <string>
#include <vector>

#include "nlohmann/json.hpp"

#include "Utility.h"

#ifdef BUILD_COMMDLL
#define COMMDLL_DLLCLASS __declspec(dllexport)
#else
#define COMMDLL_DLLCLASS __declspec(dllimport)
#endif

namespace nsRfId
{
    using stRfId = struct _stRfId
    {
        std::string Driver{ "None" };  //    = Omron600                                // Specifies the device type
        std::string ComPort{ "COM1" }; //    *Com                             Port                                          = COM3 // The com port used
        int         BaudRate{ 9600 };  //    = 9600                            // The baudrate
        int         DataBit{ 7 };      //    = 7                           // Number of data bits
        std::string Parity{ "Even" };  //    = Even                   // Parity
        double      StopBit{ 2. };     //    = 2.0              // Number of stop bits
        int         Unit{ 0 };         //    = 0            // Unit number
        int         Head{ 0 };         //    = 1 // Head
    };
}

namespace nsRfId
{
    using namespace nlohmann;

    template <typename T>
    inline void getValue(const nlohmann::json& j, const std::string& fieldname, T& value)
    {
        if (j.contains(fieldname))
        {
            j.at(fieldname).get_to(value);
        }
    }

    void from_json(const json& j, stRfId& x);
    void to_json(json& j, const stRfId& x);

    inline void from_json(const json& j, stRfId& x)
    {
        getValue(j, "Driver", x.Driver);
        getValue(j, "ComPort", x.ComPort);
        getValue(j, "BaudRate", x.BaudRate);
        getValue(j, "DataBit", x.DataBit);
        getValue(j, "Parity", x.Parity);
        getValue(j, "StopBit", x.StopBit);
        getValue(j, "Unit", x.Unit);
        getValue(j, "Head", x.Head);
    }

    inline void to_json(json& j, const stRfId& x)
    {
        j = nlohmann::json{
            { "Driver", x.Driver },
            { "ComPort", x.ComPort },
            { "BaudRate", x.BaudRate },
            { "DataBit", x.DataBit },
            { "Parity", x.Parity },
            { "StopBit", x.StopBit },
            { "Unit", x.Unit },
            { "Head", x.Head }
        };
    }
}

class CScanner;
typedef std::shared_ptr<CScanner> CommPtr;

class IRfId
{
public:
    virtual ~IRfId()
    {
    }

    virtual bool        WriteID(const std::string& strID) = 0;
    virtual bool        ReadID(std::string* pResult)      = 0;
    virtual bool        TestComm()                        = 0;
    virtual std::string GetErrorMessage() const           = 0;
};

class COMMDLL_DLLCLASS RfIdBase : public IRfId
{
private:
    void Load(int index);

    nsRfId::stRfId m_config;
    CommPtr        m_pCommPort;

public:
    // ctor
    explicit RfIdBase(int index);
    virtual ~RfIdBase();

    // methods
    long         GetBaudRate();
    long         GetByteSize();
    double       GetStopBits();
    std::string  GetParity();
    std::string  GetCommPort();
    int          GetHead();
    int          GetUnit();
    bool         Connect();
    bool         Disconnect();
    bool         IsConnected();
    bool         Send(const std::string& data);
    virtual void OnReceiveComm(const char* cpcBuffer, int nSize) = 0;
};

#pragma warning(push)
#pragma warning(disable : 4251)
class COMMDLL_DLLCLASS RfId
{
private:
    std::unique_ptr<IRfId> m_base;

public:
    // ctor
    explicit RfId(int index);
    virtual ~RfId(void);

    // methods
    bool        WriteID(const std::string& strID);
    bool        ReadID(std::string* pResult);
    bool        TestComm();
    std::string GetErrorMessage() const;
};
#pragma warning(pop)
