using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using static SystemX.Net.Platform.Common.Util.LogMessage;

namespace SystemX.Net.Platform.Common.Util
{
    public static class ConsoleUtil
    {
        static bool ConsoleShowState = false;

        const int SW_HIDE = 0;
        const int SW_SHOW = 5;

        // STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
        const int STD_INPUT_HANDLE = -10;

        /// <summary>
        /// SC_CLOSE
        /// </summary>
        private const uint SC_CLOSE = 0xf060;

        /// <summary>
        /// MF_ENABLED
        /// </summary>
        private const uint MF_ENABLED = 0x00000000;

        /// <summary>
        /// MF_GRAYED
        /// </summary>
        private const uint MF_GRAYED = 0x00000001;

        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll")]
        static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

        [DllImport("kernel32.dll")]
        static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        #region 시스템 메뉴 구하기 - GetSystemMenu(windowHandle, revert)

        /// <summary>
        /// 시스템 메뉴 구하기
        /// </summary>
        /// <param name="windowHandle">윈도우 핸들</param>
        /// <param name="revert">메뉴 복사 핸들 여부</param>
        /// <returns>시스템 메뉴 핸들</returns>
        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr windowHandle, bool revert);

        #endregion
        #region 메뉴 항목 이용 가능 여부 설정하기 - EnableMenuItem(menuHandle, menuItemID, enabled)

        /// <summary>
        /// 메뉴 항목 이용 가능 여부 설정하기
        /// </summary>
        /// <param name="menuHandle">메뉴 핸들</param>
        /// <param name="menuItemID">메뉴 항목 ID</param>
        /// <param name="enabled">이용 가능 여부</param>
        /// <returns>처리 결과</returns>
        [DllImport("user32.dll")]
        private static extern bool EnableMenuItem(IntPtr menuHandle, uint menuItemID, uint enabled);
        #endregion

        public static void ConsoleVisibleControl()
        {
            var handle = GetConsoleWindow();

            if (ConsoleShowState)
                ConsoleHide(handle);
            else
                ConsoleShow(handle);
        }

        public static void ConsoleHide()
        {
            var handle = GetConsoleWindow();
            var consoleHandle = GetStdHandle(STD_INPUT_HANDLE);

            const uint ENABLE_QUICK_EDIT = 0x0040;
            const uint ENABLE_MOUSE_INPUT = 0x0010;

            uint consoleMode = 0x0;

            // get current console mode
            if (!GetConsoleMode(consoleHandle, out consoleMode))
            {
                // Error: Unable to get console mode.
                return;
            }

            // Clear the quick edit bit in the mode flags
            consoleMode &= ~ENABLE_QUICK_EDIT;
            consoleMode &= ~ENABLE_MOUSE_INPUT;

            // set the new mode
            if (!SetConsoleMode(consoleHandle, consoleMode))
            {
                //ERROR: Unable to set console mode
                return;
            }

            // Hide
            ShowWindow(handle, SW_HIDE);

            ConsoleShowState = false;
        }

        public static void ConsoleHide(IntPtr handle)
        {
            // Hide
            ShowWindow(handle, SW_HIDE);

            ConsoleShowState = false;
        }

        public static void ConsoleShow(IntPtr handle)
        {
            // Show
            SetCloseButtonEnabled(handle, false);

            ShowWindow(handle, SW_SHOW);

            ConsoleShowState = true;
        }

        private static void SetCloseButtonEnabled(IntPtr windowHandle, bool enabled)
        {
            IntPtr systemMenuHandle = GetSystemMenu(windowHandle, false);

            EnableMenuItem(systemMenuHandle, SC_CLOSE, (uint)(MF_ENABLED | (enabled ? MF_ENABLED : MF_GRAYED)));
        }
    }
    public static class CommonUtil
    {

        public static string GetProfilePath(string programName = "", bool makeDirOnDemand = true)
        {
            var xProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            var dir = String.Format(@"{0}\TESTApp\{1}", xProfilePath, programName);
            if (makeDirOnDemand && !Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            return dir;
        }

        public static PropertyInfo GetProperty(object obj, string propName)
        {
            return obj.GetType().GetProperty(propName);
        }

        public static void ClonePropertyValues<T>(T original, T clone)
        {
            foreach (_PropertyInfo info in original.GetType().GetProperties())
                SetPropertyValue(clone, info.Name, info.GetValue(original, null));
        }

        public static T GetPropertyValue<T>(object obj, string propName)
        {
            return (T)obj.GetType().GetProperty(propName).GetValue(obj, null);
        }

        public static void SetPropertyValue(object obj, string propName, object value)
        {
            obj.GetType().GetProperty(propName).SetValue(obj, value, null);
        }

        public static Type GetType(string strType)
        {
            try
            {
                Type typeResult = Type.GetType("System." + strType, true, true);

                return typeResult;
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite("Type Recognition Error: " + ex.Message, ConsoleColor.Red, LogMessageLevel.FATAL);

                return null;
            }
        }

        public static Type GetType(Assembly assy, string strNameSpace, string strType)
        {
            try
            {
                string strFullName = $"{strNameSpace}." + strType;
                Type typeResult = Type.GetType(strFullName, true, true);

                return typeResult;
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite("Type Recognition Error: " + ex.Message, ConsoleColor.Red, LogMessageLevel.FATAL);

                return null;
            }
        }

        public static string GetStringFromASCII(int[] arrData, bool bReverseBit = false)
        {
            string sData = string.Empty;
            foreach (short s in arrData)
            {
                byte[] intBytes = BitConverter.GetBytes(s);

                byte ucRever;
                if (bReverseBit)
                {
                    ucRever = intBytes[0];
                    intBytes[0] = intBytes[1];
                    intBytes[1] = ucRever;
                }

                foreach (byte b in intBytes)
                {
                    if (b == 0)
                        continue;

                    sData += ((Char)b).ToString();             // Ascii To Char
                }
            }
            return sData;
        }

        public static short[] HexStringToBytes(string hexString)
        {
            if (hexString.Length % 2 != 0)
                hexString += "\0";

            char[] result = hexString.ToCharArray();
            short[] arrShort = new short[hexString.Length / 2];
            byte[] bytes = new byte[hexString.Length];

            for (int i = 0; i < result.Length; i++)
                bytes[i] = Convert.ToByte(result[i]);

            for (int i = 0; i < bytes.Length; i += 2)
                arrShort[i / 2] = BitConverter.ToInt16(bytes, i);

            return arrShort;
        }
    }

    public partial class NativeMethods
    {
        [DllImportAttribute("kernel32.dll", SetLastError = true, EntryPoint = "CreateFile")]
        public static extern SafeFileHandle CreateFileW(
            [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            [InAttribute()] System.IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            [InAttribute()] System.IntPtr hTemplateFile
        );

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CreateFile(
            [MarshalAs(UnmanagedType.LPTStr)] string filename,
            [MarshalAs(UnmanagedType.U4)] FileAccess access,
            [MarshalAs(UnmanagedType.U4)] FileShare share,
            IntPtr securityAttributes,
            [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
            [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
            IntPtr templateFile);
    }

    public partial class NativeConstants
    {

        /// GENERIC_WRITE -> (0x40000000L)
        public const int GENERIC_WRITE = 1073741824;

        public const uint GENERIC_READ = 2147483648;

        /// FILE_SHARE_DELETE -> 0x00000004
        public const int FILE_SHARE_DELETE = 4;

        /// FILE_SHARE_WRITE -> 0x00000002
        public const int FILE_SHARE_WRITE = 2;

        /// FILE_SHARE_READ -> 0x00000001
        public const int FILE_SHARE_READ = 1;

        /// OPEN_ALWAYS -> 4
        public const int OPEN_ALWAYS = 4;
        public const int CREATE_NEW = 1;
    }
}
