using CommandLine;
using CommandLine.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Newtonsoft.Json.Linq;
using System.Reflection;
using Newtonsoft.Json;
using System.Runtime.Remoting.Messaging;

namespace mx_uart_ctl
{
    class Program
    {
        class Options
        {
            [Option('p', Default = null, HelpText = "-p <#port index> (Start from 0)")]
            public uint? Port { get; set; }

            [Option('m', Default = null,
                HelpText = "-m <#uart mode>\n" +
                "0 --> set to RS232 mode\n" +
                "1 --> set to RS485-2W mode\n" +
                "2 --> set to RS485-4W mode\n" + 
                "3 --> set to RS422 mode"
                )]
            public uint? Mode { get; set; }

            [Option('c', Default = null, HelpText = "-c\n")]
            public uint? Count { get; set; }
        }

        [DllImport("mxsp.dll", EntryPoint = "GetUartMode", CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern int GetUartMode(int port);

        [DllImport("mxsp.dll", EntryPoint = "SetUartMode", CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern int SetUartMode(int port, int mode);

        [DllImport("mxsp.dll", EntryPoint = "GetUartCount", CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern int GetUartCount();

        [DllImport("readConfig.dll", EntryPoint = "GetModelInfo", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern void GetModelInfo(StringBuilder modelName, StringBuilder serialNumber);

        enum Uart_Mode
        {
            UART_MODE_RS232 = 0,
            UART_MODE_RS485_2W = 1,
            UART_MODE_RS485_4W = 2,
            UART_MODE_RS422 = 3,

            NOTSUPPORT = 4
        };

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

        public struct ModelInfo
        {
            public string modelName;
            public string serialNumber;
        };

        public struct Action_Struct
        {
            public int type;
            public uint port;
            public int mode;
            public int count;
        };

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

        static void ShowUartModeMessage(int type, int ret, int port)
        {
            if (type == (int)Action_Type.GET_COUNT)
            {
                Console.WriteLine(string.Format("COM port count: {0}", ret.ToString()));
            }
            else
            {
                switch (ret)
                {
                    case (int)Uart_Mode.UART_MODE_RS232:
                        Console.WriteLine("Current uart mode is RS232 interface.");
                        break;
                    case (int)Uart_Mode.UART_MODE_RS485_2W:
                        Console.WriteLine("Current uart mode is RS485-2W interface.");
                        break;
                    case (int)Uart_Mode.UART_MODE_RS485_4W:
                        if (GetMethod(port) == "mu850u")
                            Console.WriteLine("Current uart mode is RS485-4W interface.");
                        else
                            Console.WriteLine("Current uart mode is RS485-4W/RS-422 interface.");
                        break;
                    case (int)Uart_Mode.UART_MODE_RS422:
                        Console.WriteLine("Current uart mode is RS422 interface.");
                        break;
                    case (int)Uart_Mode.NOTSUPPORT:
                        Console.WriteLine("Not Support.");
                        break;
                    default:
                        Console.WriteLine("Unknown interface is set.");
                        break;
                }
            }
        }

        static void DoAction(Action_Struct action)
        {
            int ret;
            switch (action.type)
            {
                default:
                    ret = -1;
                    break;
                case (int)Action_Type.GET_MODE:
                    ret = GetUartMode((int)action.port);
                    if (ret < 0)
                    {
                        Environment.Exit(Environment.ExitCode);
                    }
                    break;
                case (int)Action_Type.SET_MODE:
                    ret = SetUartMode((int)action.port, action.mode);
                    if (ret < 0)
                    {                        
                        Environment.Exit(Environment.ExitCode);
                    }
                    Console.WriteLine("Set OK.\n");

                    ret = GetUartMode((int)action.port);
                    if (ret < 0)
                    {
                        Environment.Exit(Environment.ExitCode);
                    }
                    break;
                case (int)Action_Type.GET_COUNT:
                    ret = GetUartCount();
                    if (ret <= 0)
                    {
                        Environment.Exit(Environment.ExitCode);
                    }
                    break;
            }
            ShowUartModeMessage((int)action.type, ret, (int)action.port);
        }

        static ModelInfo GetModelInfo()
        {
            ModelInfo model = new ModelInfo();
            StringBuilder modelname = new StringBuilder();
            StringBuilder serialnum = new StringBuilder();

            string modelNameStr = string.Empty;
            string serialNumberStr = string.Empty;

            try
            {
                Task t = Task.Run(() => {
                    GetModelInfo(modelname, serialnum);
                });
                t.Wait();

                model.modelName = modelname.ToString();
                model.serialNumber = "SERIAL" + serialnum.ToString();
            }
            catch (Exception ex)
            {
                return model;
            }

            return model;
        }

        static string GetMethod(int port)
        {
            ModelInfo model = GetModelInfo();
            string filePath = string.Format(".\\MxspProfile\\MxspProfile[{0}].json", model.modelName);
            try
            {
                string jsonContent = string.Empty;
                using (StreamReader reader = new StreamReader(filePath))
                {
                    jsonContent = reader.ReadToEnd();
                }
                dynamic jsonReader = JValue.Parse(jsonContent);
                JToken token = jsonReader[model.modelName][model.serialNumber];
                string portName = (string)token["UART_PORTS"][port];
                if (!string.IsNullOrEmpty(portName))
                {
                    string method = (string)token["METHOD"][port][portName];
                    return method;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        static void Main(string[] args)
        {
            Action_Struct action = new Action_Struct();
            CommandLine.Parser.Default.ParseArguments<Options>(args)
                .WithParsed<Options>(opts =>
                {
                    if (opts.Port != null && opts.Mode == null && opts.Count == null)
                    {
                        action.type = (int)Action_Type.GET_MODE;
                        action.port = (uint)opts.Port;
                        DoAction(action);
                    }
                    else if (opts.Port != null && opts.Mode != null && opts.Count == null)
                    {
                        action.type = (int)Action_Type.SET_MODE;
                        action.port = (uint)opts.Port;
                        action.mode = (int)opts.Mode;
                        DoAction(action);
                    }
                    else if (opts.Port == null && opts.Mode == null && args.Length > 0)
                    {
                        action.type = (int)Action_Type.GET_COUNT;
                        DoAction(action);
                    }
                    else
                    {
                        ShowUsage();
                    }
                });
        }
    }
}
