/*  Copyright (C) MOXA Inc. All rights reserved.

	This software is distributed under the terms of the
	MOXA License.  See the file COPYING-MOXA for details.
*/
#include <iostream>
#include "mx-uart-ctl.h"
#include <getopt.h>  
#include <format>
#include <windows.h>
#include <filesystem>
#include <string>
#pragma comment(lib,"getopt.lib") 

#define UNSET -1

enum action_type {
	GET_MODE = 0,
	SET_MODE = 1,
	GET_COUNT = 2
};

struct action_struct {
	int type;
	int port;
	int mode;
	int count;
};

struct ModelInfo
{
	string modelName;
	string serialNumber;
};

void usage(FILE *fp)
{
	fprintf(fp, "Usage:\n");
	fprintf(fp, "	mx-uart-ctl -p <#port_number>\n");
	fprintf(fp, "	mx-uart-ctl -p <#port_number> -m <#uart_mode>\n");
	fprintf(fp, "	mx-uart-ctl -c\n");
	fprintf(fp, "OPTIONS:\n");
	fprintf(fp, "	-p <#port_number>\n");
	fprintf(fp, " \t\tSet target port: [0..]\n\n");
	fprintf(fp, "	-m <#uart_mode>\n");
	fprintf(fp, " \t\tSet target port to uart_mode\n");
	fprintf(fp, " \t\t0 --> set to RS232 mode\n");
	fprintf(fp, " \t\t1 --> set to RS485-2W mode\n");
	fprintf(fp, " \t\t2 --> set to RS485-4W mode\n");
	fprintf(fp, " \t\t3 --> set to RS422 mode\n\n");
	fprintf(fp, "	-c\n");
	fprintf(fp, " \t\tGet COM port count\n");
	fprintf(fp, "Example:\n");
	fprintf(fp, "	Get uart mode from port 2\n");
	fprintf(fp, "	# mx-uart-ctl -p 2\n");
	fprintf(fp, "	Set port 1 to mode RS422\n");
	fprintf(fp, "	# mx-uart-ctl -p 1 -m 3\n");
	fprintf(fp, "	Get COM port count\n");
	fprintf(fp, "	# mx-uart-ctl -c\n");
}

int my_atoi(const char *nptr, int *number)
{
	int tmp;

	tmp = atoi(nptr);
	if (tmp != 0) {
		*number = tmp;
		return 0;
	}
	else {
		if (!strcmp(nptr, "0")) {
			*number = 0;
			return 0;
		}
	}
	return -1;
}

ModelInfo GetModelInfo()
{
	ModelInfo modelStruct;
	char modelName[100] = { '\0' };
	char serialNumber[10] = { '\0' };

	GetModelInfo(modelName, serialNumber);

	string model(modelName);
	string number(serialNumber);

	modelStruct.modelName = model;
	modelStruct.serialNumber = "SERIAL" + number;

	return modelStruct;
}

string GetMethod(int port)
{
	ModelInfo model = GetModelInfo();
	char path[MAX_PATH] = { 0 };
	GetModuleFileNameA(NULL, path, MAX_PATH);
	std::filesystem::path exePath(path);
	string filePath = exePath.parent_path().string() + "\\MxspProfile\\MxspProfile[" + model.modelName + "].json";
	json json_device;
	try
	{
		json json_reader = ReadJson(filePath);
		json_device = json_reader[model.modelName][model.serialNumber];
		string portName = json_device["UART_PORTS"][port].begin().value();
		string method = json_device["METHOD"][port][portName].begin().value();
		return method;
	}
	catch (std::exception& e)
	{
		cout << "exception: " << e.what() << "\n";
		return "";
	}
}

void show_uart_mode_message(int type, int ret, int port)
{
	if (type == GET_COUNT)
	{
		printf("COM port count: %d\n" , ret);
	}
	else
	{
		switch (ret) {
		case UART_MODE_RS232:
			printf("Current uart mode is RS232 interface.\n");
			break;
		case UART_MODE_RS485_2W:
			printf("Current uart mode is RS485-2W interface.\n");
			break;
		case UART_MODE_RS485_4W:
			if (GetMethod(port) == "mu850u")
				printf("Current uart mode is RS485-4W interface.\n");
			else
				printf("Current uart mode is RS485-4W/RS-422 interface.\n");
			break;
		case UART_MODE_RS422:
			printf("Current uart mode is RS422 interface.\n");
			break;
		case NOTSUPPORT:
			printf("Not Support.\n");
			break;
		default:
			printf("Unknown interface is set.\n");
			break;
		}
	}
}

void do_action(struct action_struct action)
{
	int ret = 0;

	switch (action.type) {
	case GET_MODE:
		ret = GetUartMode(action.port);
		if (ret == -1) {
			printf("Unknown interface is set.\n");
			exit(1);
		}
		else if (ret == -2)
		{
			printf("Search device fail, please check device status.\n");
			exit(1);
		}
		break;
	case SET_MODE:
		ret = SetUartMode(action.port, action.mode);
		if (ret == -1) 
		{
			printf("Unknown interface is set.\n");
			exit(1);
		}
		else if (ret == -2)
		{
			printf("Search device fail, please check device status.\n");
			exit(1);
		}
		
		if (ret != 0)
		{
			printf("Error code = %d.\n", ret);
			exit(1);
		}
		else
		{
			printf("Set OK.\n\n");
		}
		
		ret = GetUartMode(action.port);
		if (ret < 0) 
		{
			printf("Unknown interface is set.\n");
			exit(1);
		}
		break;
	case GET_COUNT:
		ret = GetUartCount();
		if (ret == 0)
		{
			printf("Fail to get COM port count\n");
			exit(1);
		}
		break;
	}

	show_uart_mode_message(action.type, ret, action.port);
}

int main(int argc, char *argv[])
{
	struct action_struct action = {UNSET, UNSET, UNSET, UNSET};
	int opt;

	while ((opt = getopt(argc, argv, "hp:m:c")) != -1)
	{
		if (opt == 'c') {
			action.type = GET_COUNT;
		}
		else if (opt == 'h') {
			usage(stdout);
			exit(0);
		}
		else if (opt == 'm') {
			if (my_atoi(optarg, &action.mode) != 0) {
				fprintf(stderr, "%s is not a number\n", optarg);
				exit(1);
			}
			action.type = SET_MODE;
		}
		else if (opt == 'p') {
			if (my_atoi(optarg, &action.port) != 0) {
				fprintf(stderr, "%s is not a number\n", optarg);
				exit(1);
			}
			if (action.mode == UNSET)
				action.type = GET_MODE;
			else
				action.type = SET_MODE;
		}
	}

	if (optind < argc) {
		fprintf(stderr, "non-option arguments found: ");
		while (optind < argc)
			fprintf(stderr, "%s ", argv[optind++]);
		fprintf(stderr, "\n");

		usage(stderr);
		exit(99);
	}

	if (action.port == UNSET && action.mode == UNSET && action.type == UNSET||
		action.port == UNSET && action.mode != UNSET) {
		usage(stderr);
		exit(99);
	}

	do_action(action);
	exit(0);
}

