#include <cstdio>
#include <cstdint>

#include <unistd.h>

#include "../common/CSerial.h"
#include "../common/CSocket.h"
#include "../common/LE9xxCommand.h"

CComm *CreateIO()
{
	// -------------------------------------------------
	// Select the communication method with LE-930R.
	// For the USB interface, open the virtual COM port.
	// For the WiFi interfaces, open a TCP socket.
	// -------------------------------------------------
#if 1
	// Open COM port
	CSerial *comm = new CSerial();
	char device[] = "/dev/ttyUSB0";
	if (!comm->Open((void *)device))
	{
		printf("Interface open error.\n");
		delete comm;
		return nullptr;
	}
#else
	// Open TCP socket
	// (IP Address = 192.168.0.130, Control port number = 10101)
	CSocket *comm = new CSocket();
	CSocket::CONNECTION connetion = {"192.168.0.130", 10101};
	if (!comm->Open((void *)&connetion))
	{
		printf("Interface open error.\n");
		delete comm;
		return nullptr;
	}
#endif

	return comm;
}

LE9xxCommand *Connect(CComm *comm)
{
	// ------------------
	// Connect to LE-930R
	// ------------------
	FRAME Res;
	LE9xxCommand *cmd = new LE9xxCommand(comm);

	// Invalid connection maintenance command transmission from LE-930R
	cmd->TransmitCommandWait(CMD_CONNECT, 0x20, 0, NULL, &Res);
	if ((Res.res != RES_OK) && (Res.res != RES_ALREADY_CONNECTED))
	{
		printf("Connection error. [0x%02X]\n", Res.res);
		delete cmd;
		return nullptr;
	}
	printf("Connection OK.\n");

	return cmd;
}

bool Output(LE9xxCommand *cmd)
{
	// -----------------
	// Setup for LE-930R
	// ------------------
	// Input output type
	char input[64] = "";
	LE930_TYPE type;
	printf("Enter the output type number.\n");
	printf(" %d : 100mV range\n", LE930_TYPE_VOLTAGE_100mV);
	printf(" %d : 10V range\n", LE930_TYPE_VOLTAGE_10V);
	printf(" %d : 4-20mA range (Use internal power supply)\n", LE930_TYPE_CURRENT_INT);
	printf(" %d : 4-20mA range (Use external power supply)\n", LE930_TYPE_CURRENT_EXT);
	printf("> ");
	scanf("%d%*[^\n]", &type);
	if ((type < 0) || (type >= LE930_TYPE_NUM))
	{
		printf("The input number is invalid.\n");
		return false;
	}

	float min, max, pointA, pointB;
	uint32_t T1, T2;
	char *unit;
	switch (type)
	{
	case LE930_TYPE_VOLTAGE_100mV:
		min = -100;
		max = 100;
		unit = "mV";
		break;
	case LE930_TYPE_VOLTAGE_10V:
		min = -10;
		max = 10;
		unit = "V";
		break;
	case LE930_TYPE_CURRENT_INT:
	case LE930_TYPE_CURRENT_EXT:
		min = 0;
		max = 20;
		unit = "mA";
		break;
	}

	// Input point A
	printf("Enter the point A output value in the range of %.0f%s to %.0f%s.\n", min, unit, max, unit);
	printf("> ");
	scanf("%f%*[^\n]", &pointA);
	if ((pointA < min) || (pointA > max))
	{
		printf("Input value is invalid.\n");
		return false;
	}

	// Input point B
	printf("Enter the point B output value in the range of %.0f%s to %.0f%s.\n", min, unit, max, unit);
	printf("> ");
	scanf("%f%*[^\n]", &pointB);
	if ((pointB < min) || (pointB > max))
	{
		printf("Input value is invalid.\n");
		return false;
	}

	// Input T1
	printf("Enter the T1 value in the range of 0 to 60000.\n");
	printf("> ");
	scanf("%d%*[^\n]", &T1);
	if ((T1 < 0) || (T1 > 60000))
	{
		printf("Input value is invalid.\n");
		return false;
	}

	// Input T2
	printf("Enter the T2 value in the range of 0 to 60000.\n");
	printf("> ");
	scanf("%d%*[^\n]", &T2);
	if ((T2 < 0) || (T2 > 60000))
	{
		printf("Input value is invalid.\n");
		return false;
	}

	// ------------------------------
	// Sweep setting and output start
	// ------------------------------
	FRAME Res;
	uint8_t sweep[9];
	sweep[0] = (uint8_t)type;

	uint32_t ad = cmd->ConvertRealDataLE930(type, pointA);
	sweep[1] = (uint8_t)(ad >> 8);
	sweep[2] = (uint8_t)(ad & 0xFF);

	ad = cmd->ConvertRealDataLE930(type, pointB);
	sweep[3] = (uint8_t)(ad >> 8);
	sweep[4] = (uint8_t)(ad & 0xFF);

	sweep[5] = (uint8_t)(T1 >> 8);
	sweep[6] = (uint8_t)(T1 & 0xFF);

	sweep[7] = (uint8_t)(T2 >> 8);
	sweep[8] = (uint8_t)(T2 & 0xFF);

	cmd->TransmitCommandWait(CMD_DA_SWEEP_SET, 0x00, sizeof(sweep), sweep, &Res);
	if (Res.res != RES_OK)
	{
		printf("Analog sweep output setting error. [0x%02X]\n", Res.res);
		return false;
	}

	// Acquires the current output value of LE-930R every second.
	// The measurement is performed 10 times.
	for (int i = 0; i < 10; i++)
	{
		// Get the current output value
		cmd->TransmitCommandWait(CMD_DA_OUT_GET, 0x00, 0, NULL, &Res);
		if (Res.res != RES_OK)
		{
			printf("\nCheck analog output setting error. [0x%02X]\n", Res.res);
			break;
		}

		// Conversion of analog data
		uint32_t ad;
		ad = (uint32_t)Res.data[2] << 8;
		ad |= (uint32_t)Res.data[3];
		float value = cmd->ConvertAnalogDataFromLE930(type, ad);

		switch (type)
		{
		case LE930_TYPE_VOLTAGE_100mV:
			printf("%.2f[mV]", value);
			break;
		case LE930_TYPE_VOLTAGE_10V:
			printf("%.3f[V]", value);
			break;
		case LE930_TYPE_CURRENT_INT:
		case LE930_TYPE_CURRENT_EXT:
			printf("%.3f[mA]", value);
			break;
		}
		printf("\n");
		sleep(1);
	}

	// Stop output
	uint8_t vout[3] = {0};
	vout[0] = (uint8_t)type;
	cmd->TransmitCommandWait(CMD_DA_OUT_SET, 0x00, sizeof(vout), vout, &Res);
	if (Res.res != RES_OK)
	{
		printf("Analog output error. [0x%02X]\n", Res.res);
		return false;
	}

	return true;
}

bool Disconnect(LE9xxCommand *cmd)
{
	// ----------------------
	// Disonnect from LE-930R
	// ----------------------
	FRAME Res;
	cmd->TransmitCommandWait(CMD_DISCONNECT, 0x00, 0, NULL, &Res);
	if (Res.res != RES_OK)
	{
		printf("Disconnection error. [0x%02X]\n", Res.res);
		return false;
	}
	printf("Disconnection OK.\n");

	return true;
}

int main()
{
	int ret = -1;

	CComm *comm = CreateIO();
	if (!comm)
	{
		return -1;
	}

	LE9xxCommand *cmd = Connect(comm);
	if (!cmd)
	{
		goto error_comm;
	}

	if (!Output(cmd))
	{
		goto error_comm;
	}

	ret = 0;

error_cmd:
	Disconnect(cmd);

error_comm:
	comm->Close();
	delete cmd;

error_exit:
	delete comm;

	return ret;
}
