using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AgkSharp;

namespace AGKCore
{
    public class Core
    {
        static Form m_Window;
        static bool m_bMaximized = false;
        static bool m_bBreakLoop = false;
        public static bool m_bDisableEscape = false;

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetKeyboardState(byte[] lpKeyState);
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint MapVirtualKey(uint uCode, uint uMapType);
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetKeyboardLayout(uint idThread);
        [DllImport("user32.dll", SetLastError = true)]
        static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        public static Form CreateWin32Window(string title, int width, int height, bool fullscreen)
        {

            m_Window = new Form
            {
                Text = title,
                ClientSize = new Size(width, height),
                StartPosition = FormStartPosition.CenterScreen,
            };

            if (fullscreen)
            {
                m_Window.WindowState = FormWindowState.Normal;
                m_Window.FormBorderStyle = FormBorderStyle.None;
                m_Window.Bounds = Screen.PrimaryScreen.Bounds;
            }

            if (System.IO.File.Exists("icon.ico"))
                m_Window.Icon = new Icon("icon.ico");

            m_Window.MouseDown += Core_OnMouseDown;
            m_Window.MouseUp += Core_OnMouseUp;
            m_Window.MouseWheel += Core_OnMouseWheel;
            m_Window.MouseMove += Core_OnMouseMove;
            m_Window.SizeChanged += Core_OnSizeChanged;
            m_Window.Move += Core_OnMove;
            m_Window.Activated += Core_OnActivated;
            m_Window.Deactivate += Core_OnDeactivate;
            m_Window.GotFocus += Core_OnGotFocus;
            m_Window.LostFocus += Core_OnLostFocus;
            m_Window.KeyDown += Core_OnKeyDown;
            m_Window.KeyUp += Core_OnKeyUp;
            m_Window.FormClosing += Core_OnClose;


            m_Window.Show();
            //window.KeyDown += OnKeyDown;

            return m_Window;
        }

        static int TranslateKey(int key)
        {
            switch (key)
            {
                case 20: key = 0; break; // Caps lock
                case 120: key = 0; break; // F9
                case 121: key = 0; break; // F10
                case 122: key = 0; break; // F11
                case 123: key = 0; break; // F12
                case 18: key = 18; break; // Alt Gr
                case 91: key = 0; break; // Windows key (left)
                case 92: key = 0; break; // Windows key (right)

                // Num pad 0-9
                case 96:
                case 97:
                case 98:
                case 99:
                case 100:
                case 101:
                case 102:
                case 103:
                case 104:
                case 105: key -= 48; break;

                case 111: key = 192; break; // Num pad /
                case 109: key = 189; break; // Num pad -
                case 110: key = 190; break; // Num pad .

                case 144: key = 0; break;  // Num lock
                case 19: key = 0; break;  // pause/break
            }

            return key;
        }

        static Char TranslateKeyToUnicode(Keys key)
        {
            Char result = new Char();

            byte[] keyboardState = new byte[255];
            bool keyboardStateStatus = GetKeyboardState(keyboardState);

            if (!keyboardStateStatus)
            {
                return result;
            }

            uint virtualKeyCode = (uint)key;
            uint scanCode = MapVirtualKey(virtualKeyCode, 0);
            IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);

            StringBuilder txt = new StringBuilder();
            ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, txt, (int)5, (uint)0, inputLocaleIdentifier);
            if (txt.Length > 0)
                result = txt[0];

            return result;

        }

        static void Core_OnClose(object sender, EventArgs e)
        {
            m_bBreakLoop = true;
        }

        static void Core_OnMouseMove(object sender, MouseEventArgs e)
        {
            Agk.MouseMove(0, e.X, e.Y);
        }

        static void Core_OnMouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) Agk.MouseLeftButton(0, 1);
            if (e.Button == MouseButtons.Right) Agk.MouseRightButton(0, 1);
            if (e.Button == MouseButtons.Middle) Agk.MouseMiddleButton(0, 1);
        }

        static void Core_OnMouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) Agk.MouseLeftButton(0, 0);
            if (e.Button == MouseButtons.Right) Agk.MouseRightButton(0, 0);
            if (e.Button == MouseButtons.Middle) Agk.MouseMiddleButton(0, 0);
        }

        static void Core_OnMouseWheel(object sender, MouseEventArgs e)
        {
            Agk.MouseWheel(0, (float)e.Delta / 40);
        }

        static void Core_OnSizeChanged(object sender, EventArgs e)
        {
            m_bMaximized = m_Window.WindowState == FormWindowState.Maximized;
            m_Window.TopMost = m_bMaximized;
            m_Window.Update();

            Agk.UpdateDeviceSize();
            Agk.WindowMoved();
        }

        static void Core_OnMove(object sender, EventArgs e)
        {
            Agk.WindowMoved();
        }

        static void Core_OnDeactivate(object sender, EventArgs e)
        {
            Agk.MouseLeftButton(0, 0);
            Agk.MouseRightButton(0, 0);
            Agk.MouseMiddleButton(0, 0);

            m_Window.TopMost = m_bMaximized;
            m_Window.Update();
            Agk.Paused();

            Agk.WindowMoved();
        }

        static void Core_OnActivated(object sender, EventArgs e)
        {
            m_Window.TopMost = m_bMaximized;
            m_Window.Update();
            Agk.Resumed();

            Agk.WindowMoved();
        }

        static void Core_OnGotFocus(object sender, EventArgs e)
        {
            Agk.WindowMoved();
        }

        static void Core_OnLostFocus(object sender, EventArgs e)
        {
            Agk.WindowMoved();
        }

        static void Core_OnKeyDown(object sender, KeyEventArgs e)
        {
            int key = TranslateKey(e.KeyValue);
            if (key > 0)
                Agk.KeyDown((uint)key);

            if (!m_bDisableEscape && key == 27)
                m_bBreakLoop = true;

            Char chr = TranslateKeyToUnicode(e.KeyCode);
            Agk.CharDown(chr);
        }

        static void Core_OnKeyUp(object sender, KeyEventArgs e)
        {
            int key = TranslateKey(e.KeyValue);
            if (key > 0)
                Agk.KeyUp((uint)key);
        }

        public static bool InitAGK(Form window)
        {
            if (window.Handle != null)
                Agk.InitGL(window.Handle);

            return !m_bBreakLoop;
        }

        public static bool InitAGK()
        {
            if (m_Window.Handle != null)
                Agk.InitGL(m_Window.Handle);

            return !m_bBreakLoop;
        }

        public static bool LoopAGK()
        {
            Application.DoEvents();

            if (Agk.IsCapturingImage() > 0)
                System.Threading.Thread.Sleep(10);

            return !m_bBreakLoop;
        }

        public static void CleanUp()
        {
            Agk.CleanUp();
        }
    }
}
