using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace JtkLocalServerLauncher {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
            SetupCustomFont();
        }

        private async void launchButton_Click(object sender, EventArgs e) {
            outputTextBox.BackColor = SystemColors.Window;
            argumentsGroupBox.Enabled = false;
            launchButton.Enabled = false;
            stopCheckBox.Enabled = true;
            outputTextBox.Text = string.Empty;
            await Launch();
            outputTextBox.BackColor = SystemColors.Control;
            argumentsGroupBox.Enabled = true;
            launchButton.Enabled = true;
            stopCheckBox.Enabled = false;
            stopCheckBox.Checked = false;
        }

        unsafe private void SetupCustomFont() {
            PrivateFontCollection pfc = new PrivateFontCollection();
            var bytes = Properties.Resources.D2Coding;
            fixed (byte * ptr = bytes) {
                pfc.AddMemoryFont((IntPtr)ptr, bytes.Length);
            }
            var family = pfc.Families[0];
            outputTextBox.Font = new Font(family.Name, 9f, FontStyle.Regular, GraphicsUnit.Point);
        }

        private void KillProcessAndChildrens(int pid) {
            ManagementObjectSearcher processSearcher = new ManagementObjectSearcher ("Select * From Win32_Process Where ParentProcessID=" + pid);
            ManagementObjectCollection processCollection = processSearcher.Get();

            // We must kill child processes first!
            if (processCollection != null) {
                foreach (ManagementObject mo in processCollection) {
                    KillProcessAndChildrens(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
                }
            }

            // Then kill parents.
            try {
                Process proc = Process.GetProcessById(pid);
                if (!proc.HasExited) {
                    proc.Kill();
                }
            } catch (ArgumentException) {
                // Process already exited.
            }
        }

        private Task Launch() => Task.Factory.StartNew(() => {
            void dataReceivedEventHandler(object sender, DataReceivedEventArgs e) {
                outputTextBox.Invoke(new Action<string>(data => {
                    if (data != null) {
                        outputTextBox.AppendText(data + "\r\n");
                        outputTextBox.Select(outputTextBox.Text.Length, 0);
                    }
                }), e.Data);
            }
            Thread.CurrentThread.IsBackground = false;
            using (var process = new Process()) {
                var serializedArgs = $"{addressTextbox.Text}\n{idTextBox.Text}\n{passwordTextBox.Text}\n{(testCheckBox.Checked ? 1 : 0)}";
                var encodedArgs = Convert.ToBase64String(Encoding.UTF8.GetBytes(serializedArgs), Base64FormattingOptions.None);
                process.StartInfo.FileName = "dotnet";  // dotnet이 쉘 내부에서 실행되어 총 두개의 프로세스 생성됨에 유의
                process.StartInfo.Arguments = compileCheckBox.Checked ? $"run {encodedArgs} --launch-profile \"Kestrel Development\"" : $"LocalServer.dll {encodedArgs}";
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.OutputDataReceived += dataReceivedEventHandler;
                process.ErrorDataReceived += dataReceivedEventHandler;

                process.Start();
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                var closed = false;
                while (closed == false && process.HasExited == false) {
                    if (this.IsDisposed || stopCheckBox.Checked) {
                        //KillProcessAndChildrens(process.Id);
                        //process.StandardInput.Close();
                        //process.Kill();
                        closed = true;
                    }
                    Thread.Sleep(50);
                }
                KillProcessAndChildrens(process.Id);
                //process.WaitForExit();
            }
        });

        private void Form1_FormClosed(object sender, FormClosedEventArgs e) {

            Dispose();
        }

        private void stopCheckBox_CheckedChanged(object sender, EventArgs e) {
            if (stopCheckBox.Checked == true) {
                stopCheckBox.Enabled = false;
            }
        }
    }
}
