#pragma once

#include <vector>

#include "nlohmann/json.hpp"

#include "HardwareConfiguration.h"

#include "DllDefines.h"

/**
 * @brief json format
 * @summary
 *  {
 *   "InputCard":[
 *		{
 *			"Type": "7851",
 *			"NumOfCard": 1,
 *          "CardCfg": [
 *			 {
 *				"Channels": [
 *					[ 0,0,0,0,0],
 *					[ 0,0,0,0,0],
 *					[ 0,0,0,0,0],
 *					[ 0,0,0,0,0]
 *				]
 *			 }
 *			]
 *		}
 *	],
 *   "OutputCard":[
 *		{
 *			"Type": "7434",
 *			"NumOfCard": 1
 *		}
 *	],
 *   "MotionCard":[
 *		{
 *			"Type": "8164",
 *			"NumOfCard": 2
 *		},
 *		{
 *			"Type": "ADLINK_APS",
 *			"NumOfCard": 1,
 *			"DistributedMotionCfg":[
 *				{
 *					"Type": "7856",
 *					"CardCfg":[
 *					 {
 *						"Address": 1,
 *						"HSLSpeed": 1,
 *						"HSLHubNumber": 2,
 *						"MotionNetSpeed":3
 *					 }
 *					]
 *				}
 *			]
 *		}
 *	],
 *   "EncoderCard":[
 *		{
 *			"Type": "8133",
 *			"NumOfCard": 1
 *		}
 *	],
 * "Galil":{
 *   "TriggerPulseWidth":2,
 *   "NewHomeBySensor":false
 * }
 *
 *  }
 **/

namespace nsHardware
{
    using stMotionNetCfg = struct _stMotionNetCfg
    {
        // for Adlink motionnet & advantech ethercat
        int Address{ -1 };
        // for motionet
        int HSLSpeed{ 2 };     // 1=3Mbps, 2=6Mpbs, 3=12Mbps
        int HSLHubNumber{ 0 }; // 0-7
        int MotionNetSpeed{ 3 };
    };

    using stDistributedMotionCfg = struct _stDistributedMotionCfg
    {
        std::string                 Type{ "ERROR" };
        std::vector<stMotionNetCfg> CardCfg;
    };

    using stDIOCardCfg = struct _stDIOCardCfg
    {
        // For 7296 DIO
        std::vector<std::vector<int>> Channels{};
    };

    using stGalilCardCfg = struct _stGalilCardCfg
    {
        int  TriggerPulseWidth{ 2 };
        bool NewHomeBySensor{ false };
    };

    using stHardwareCfg = struct _stHardwareCfg
    {
        std::string Type{ "ERROR" };
        int         NumOfCard{ 0 };
        // for DIO 7296
        std::vector<stDIOCardCfg> CardCfg{};
        // For Distrbuted Motion (MotionNet / Advantech Ethercat)
        std::vector<stDistributedMotionCfg> DistributedMotionCfg{};
    };

    using stHardware = struct _stHardware
    {
        std::vector<stHardwareCfg> InputCard;
        std::vector<stHardwareCfg> OutputCard;
        std::vector<stHardwareCfg> MotionCard;
        std::vector<stHardwareCfg> EncoderCard;
        // custom obtion for Galil Card
        stGalilCardCfg Galil;
    };
}

namespace nsHardware
{
    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, stMotionNetCfg& x);
    void to_json(json& j, const stMotionNetCfg& x);

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

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

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

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

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

    inline void from_json(const json& j, stMotionNetCfg& x)
    {
        getValue(j, "Address", x.Address);
        getValue(j, "HSLSpeed", x.HSLSpeed);
        getValue(j, "HSLHubNumber", x.HSLHubNumber);
        getValue(j, "MotionNetSpeed", x.MotionNetSpeed);
    }

    inline void to_json(json& j, const stMotionNetCfg& x)
    {
        j = nlohmann::json{
            { "Address", x.Address },
            { "HSLSpeed", x.HSLSpeed },
            { "HSLHubNumber", x.HSLHubNumber },
            { "MotionNetSpeed", x.MotionNetSpeed }
        };
    }

    inline void from_json(const json& j, stDistributedMotionCfg& x)
    {
        getValue(j, "Type", x.Type);
        getValue(j, "CardCfg", x.CardCfg);
    }

    inline void to_json(json& j, const stDistributedMotionCfg& x)
    {
        j = nlohmann::json{
            { "Type", x.Type },
            { "CardCfg", x.CardCfg }
        };
    }

    inline void from_json(const json& j, stDIOCardCfg& x)
    {
        getValue(j, "Channels", x.Channels);
    }

    inline void to_json(json& j, const stDIOCardCfg& x)
    {
        j = nlohmann::json{
            { "Channels", x.Channels }
        };
    }

    inline void from_json(const json& j, stGalilCardCfg& x)
    {
        getValue(j, "TriggerPulseWidth", x.TriggerPulseWidth);
        getValue(j, "NewHomeBySensor", x.NewHomeBySensor);
    }

    inline void to_json(json& j, const stGalilCardCfg& x)
    {
        j = nlohmann::json{
            { "TriggerPulseWidth", x.TriggerPulseWidth },
            { "NewHomeBySensor", x.NewHomeBySensor }
        };
    }

    inline void from_json(const json& j, stHardwareCfg& x)
    {
        getValue(j, "Type", x.Type);
        getValue(j, "NumOfCard", x.NumOfCard);
        getValue(j, "CardCfg", x.CardCfg);
        //
        getValue(j, "DistributedMotionCfg", x.DistributedMotionCfg);
    }

    inline void to_json(json& j, const stHardwareCfg& x)
    {
        j = nlohmann::json{
            { "Type", x.Type },
            { "NumOfCard", x.NumOfCard },
            { "CardCfg", x.CardCfg },
            { "DistributedMotionCfg", x.DistributedMotionCfg }
        };
    }

    inline void from_json(const json& j, stHardware& x)
    {
        getValue(j, "InputCard", x.InputCard);
        getValue(j, "OutputCard", x.OutputCard);
        getValue(j, "MotionCard", x.MotionCard);
        getValue(j, "EncoderCard", x.EncoderCard);
        //
        getValue(j, "Galil", x.Galil);
    }

    inline void to_json(json& j, const stHardware& x)
    {
        j = nlohmann::json{
            { "InputCard", x.InputCard },
            { "OutputCard", x.OutputCard },
            { "MotionCard", x.MotionCard },
            { "EncoderCard", x.EncoderCard },
            //
            { "Galil", x.Galil }
        };
    }
}

class IODLLCLASS CHardwareJson : public CHardwareConfiguration
{
public:
    // ctor
    CHardwareJson();
    virtual ~CHardwareJson();

    // Inherited via CHardwareConfiguration
    /**
     * @brief For DIO 7496 to configure io point type (input / output)
     * @summary 0 = None, 1 = Input, 2 = Output
     *
     * @param nCardType 1-n
     * @param nCardNo 1-n
     * @param nNoOfChannel up to 5
     * @param [out] npChannelArray
     * @param nWhatCard
     *
     * @returns true = successfull
     */
    bool        GetChannelConfig(int nCardType, int nCardNo, int nNoOfChannel, std::vector<int>& vecChannelArray, int nWhatCard) override;
    int         GetSharing(int nCardType, int nCardNo, int nWhatCard) override;
    int         GetNoOfDifferentCardType(int nWhatCard) override;
    std::string GetCardName(int nCardTypeNo, int nWhatCard) override;
    int         GetNoOfCard(int nCardTypeNo, int nWhatCard) override;

private:
    void Load();

    std::vector<nsHardware::stHardwareCfg>& CHardwareJson::GetHardware(int nWhatCard);

    nsHardware::stHardware m_hardware;

    // Inherited via CHardwareConfiguration
    int         GetNoOfModels(int nCardTypeNo, int nWhatCard) override;
    std::string GetModelName(int nCardTypeNo, int nModelNo, int nWhatCard) override;
    int         GetModelNoOfCards(int nCardTypeNo, int nModelNo, int nWhatCard) override;
    int         GetAddress(int nCardTypeNo, int nModelNo, int nCardNo, int nWhatCard) override;
    int         GetSpeed(int nCardTypeNo, int nModelNo, int nCardNo, int nWhatCard) override;
    int         GetHSLSpeed(int nCardTypeNo, int nModelNo, int nCardNo, int nWhatCard) override;
    int         GetHSLHubNumber(int nCardTypeNo, int nModelNo, int nCardNo, int nWhatCard) override;

    // Inherited via CHardwareConfiguration
    int GetGalilTriggerPulseWidth() override;

    // Inherited via CHardwareConfiguration
    bool GetGalilNewHomeBySensor() override;
};
