#pragma once

#include <cstdint>
#include <vector>
#include <memory>

#include <pthread.h>

#include "CComm.h"
#include "CSerial.h"
#include "CSocket.h"

// Command code
#define CMD_CONNECT 0x10		  // Connect
#define CMD_DISCONNECT 0x11		  // Disonnect
#define CMD_DIN_GET 0x90		  // DIN state acquisition
#define CMD_AD_SPS_SET 0xB0		  // AD converter conversion speed setting
#define CMD_AD_RANGE_SET 0xB1	  // Analog input range setting
#define CMD_AD_CYCLE_SET 0xB2	  // Analog transfer cycle setting
#define CMD_AD_VALUE_GET 0xB4	  // Analog data acquisition
#define CMD_AD_RUN 0xB5			  // Start analog measurement
#define CMD_AD_STOP 0xB6		  // Stop analog measurement
#define CMD_AD_RUN_RESULT 0xB7	  // Analog measurement start notification
#define CMD_AD_STOP_RESULT 0xB8	  // Analog measurement stop notification
#define CMD_AD_TRANSFER_DATA 0xB9 // Analog measurement data transmission
#define CMD_DA_OUT_SET 0xC1		  // Analog output settings
#define CMD_DA_OUT_GET 0xC2		  // Check analog output settings
#define CMD_DA_SWEEP_SET 0xC6	  // Analog sweep output settings
#define CMD_TEMP_SET 0xD0		  // Thermocouple measurement settings

// Response code
#define RES_OK 0x00				   // Success
#define RES_CHKSUM_ERR 0x01		   // Checksum error
#define RES_FRM_ERR 0x02		   // Frame error
#define RES_PARAM_ERR 0x03		   // Parameter error
#define RES_NO_CONNECT 0x04		   // No connection
#define RES_ALREADY_CONNECTED 0x05 // Already connected
#define RES_OTHER_CONNECTED 0x06   // Another interface is already connected
#define RES_CAN_NOT_DISCONNEC 0x07 // Cannot be disconnected
#define RES_UNIMPLEMENT 0x08	   // Unsupported commands
#define RES_RUNNING 0x09		   // Not accepted
#define RES_EEPROM_ERR 0x0A		   // EEPROM access error
#define RES_UNKNOWN 0xFF		   // Undefined command

// FRAME data structure
struct FRAME
{
	uint8_t sof; // SOF
	uint8_t cmd; // Command code
	union
	{
		uint8_t sub; // Sub command code
		uint8_t res; // Response code
	};
	uint32_t length;	// Data length
	uint8_t data[1024]; // Data
	uint8_t sum;		// Checksum
};

// Input RANGE
enum LE910_RANGE
{
	LE910_RANGE_100mV,
	LE910_RANGE_1V,
	LE910_RANGE_10V,
	LE910_RANGE_30V,
	LE910_RANGE_I_250ohm,
	LE910_RANGE_I_50ohm,
	LE910_RANGE_TC,
	LE910_RANGE_NUM
};

// Input RANGE
enum LE928_RANGE
{
	LE928_RANGE_4V,
	LE928_RANGE_8V,
	LE928_RANGE_16V,
	LE928_RANGE_30V,
	LE928_RANGE_60V,
	LE928_RANGE_NUM
};

// Output TYPE for LE-930R
enum LE930_TYPE
{
	LE930_TYPE_VOLTAGE_100mV, // 100mV range
	LE930_TYPE_VOLTAGE_10V,	  // 10V range
	LE930_TYPE_CURRENT_INT,	  // 4-20mA range (Use internal power supply)
	LE930_TYPE_CURRENT_EXT,	  // 4-20mA range (Use external power supply)
	LE930_TYPE_NUM,
};

// Output TYPE for LE-940R
enum LE940_TYPE
{
	LE940_TYPE_VOLTAGE_32V, // 32V range
	LE940_TYPE_VOLTAGE_32V_1,
	LE940_TYPE_CURRENT, // 4-20mA range
	LE940_TYPE_CURRENT_1,
	LE940_TYPE_NUM
};

class LE9xxCommand
{
public:
	LE9xxCommand(CComm *comm);
	~LE9xxCommand();
	uint32_t AnalyzeFrameData();
	void TransmitCommand(uint8_t cmd, uint8_t sub, uint32_t length, uint8_t *pdata);
	void TransmitResponse(uint8_t cmd, uint8_t sub, uint32_t length, uint8_t *pdata);
	void TransmitCommandWait(uint8_t cmd, uint8_t sub, uint32_t length, uint8_t *pdata, FRAME *pRes);
	void ReceiveCommandWait(uint8_t cmd, FRAME *pRes);
	float ConvertAnalogDataFromLE910(LE910_RANGE range, uint32_t ad);
	float ConvertAnalogDataFromLE928(LE928_RANGE range, uint32_t ad);
	float ConvertAnalogDataFromLE930(LE930_TYPE type, uint32_t ad);
	uint32_t ConvertRealDataLE930(LE930_TYPE type, float val);
	uint32_t ConvertRealDataLE940(LE940_TYPE type, float val);
	bool GetResponseFrame(uint8_t code, FRAME *pframe);
	bool GetCommandFrame(uint8_t code, FRAME *pframe);

private:
	typedef struct
	{
		bool fSet;
		uint8_t bCode;
		FRAME Frame;
	} CMDRES_STAT;

	static void *ThreadFunc(void *p);

	void TransmitCommandResponse(uint8_t sof, uint8_t cmd, uint8_t subres, uint32_t length, uint8_t *pdata);
	uint32_t AddResponseList(uint8_t code);
	void SetResponseFrame(uint8_t code, FRAME *pframe);
	uint32_t AddCommandList(uint8_t code);
	void SetCommandFrame(uint8_t code, FRAME *pframe);

	pthread_t thread_;
	pthread_mutex_t mutex_;
	bool terminated_;
	CComm *comm_;
	std::vector<CMDRES_STAT> response_list_;
	std::vector<CMDRES_STAT> command_list_;
};
