using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using ff14bot;
using ff14bot.Behavior;
using ff14bot.Helpers;
using ff14bot.Interfaces;
using ff14bot.Managers;
using ff14bot.Settings;
using Microsoft.CSharp;


namespace HighVoltz
{
    public class RebornConsole : IBotPlugin
    {
        public static string TempFolder = Path.Combine(GlobalSettings.Instance.PluginsPath, @"RebornConsole\Temp\");//Utils.AssemblyDirectory + @"\Plugins\RebornConsole\Temp\";
        internal static AppDomainDriver CodeDriver = new AppDomainDriver();
        private readonly Version _version = new Version(1, 10);
        private Thread newThread;
        public RebornConsole()
        {
            Instance = this;
            if (!_init)
            {
                //logr = Logger.GetLoggerInstanceForType();
                // using ctor so plugin doesn't need to be 'Enabled' to initialize..
                try
                {
                    WipeTempFolder();
                }
                catch (Exception ex)
                {
                    //logr.Debug(ex.ToString());
                }




                _init = true;
            }

        }


        public bool Equals(IBotPlugin other)
        {
            throw new NotImplementedException();
        }


        string IBotPlugin.Author { get { return "HighVoltz"; } }

        public string Name
        {
            get
            {
                return "RebornConsole";
            }
        }

        public string Description { get; private set; }

        public bool WantButton
        {
            get { return true; }
        }

        public string ButtonText
        {
            get { return "meow"; }
        }


        public void OnPulse()
        {
            //throw new NotImplementedException();
        }




        public void OnInitialize()
        {

        }

        public void OnShutdown()
        {
            //throw new NotImplementedException();
        }

        public void OnEnabled()
        {
            ToggleRBConsole();
            //throw new NotImplementedException();
        }

        public void OnDisabled()
        {
            //throw new NotImplementedException();
        }

        public string Author { get; private set; }

        public Version Version
        {
            get
            {
                return new Version(0, 0, 1);
            }
        }


        private static bool _init;


        public static RebornConsole Instance { get; private set; }



        private void WipeTempFolder()
        {
            if (!Directory.Exists(TempFolder))
            {
                Directory.CreateDirectory(TempFolder);
            }
            foreach (string file in Directory.GetFiles(TempFolder, "*.*", SearchOption.AllDirectories))
            {
                try
                {
                    File.Delete(file);
                }
                // ReSharper disable EmptyGeneralCatchClause
                catch
                {
                }
                // ReSharper restore EmptyGeneralCatchClause
            }
        }

        public void OnButtonPress()
        {
            ToggleRBConsole();
        }





        internal Thread _guiThread;
        public void ToggleRBConsole()
        {
            if (_guiThread == null || !_guiThread.IsAlive)
            {
                _guiThread = new Thread(() => new MainForm().ShowDialog()) { IsBackground = true };
                _guiThread.SetApartmentState(ApartmentState.STA);
                _guiThread.Start();
            }
            else
            {
                //if (MainForm.IsValid)
                //    MainForm.Instance.Close();
                //_guiThread = null;
                _guiThread.Abort();
            }
        }

        public static void Api(string apiName)
        {
            Type[] types = Assembly.GetEntryAssembly().GetExportedTypes().Where(t => t.Name.Contains(apiName)).ToArray();

            foreach (Type t in types)
            {
                Log(Colors.DarkGreen, "\n  *** {0} ***", t.FullName);
                foreach (MemberInfo mi in t.GetMembers())
                {
                    Log("{0}", mi);
                }
            }
        }

        public static void Log(string text, params object[] arg)
        {
            if (!string.IsNullOrEmpty(text))
                Log(Colors.Black, text, arg);
        }
        public static void Log(string text)
        {
            if (!string.IsNullOrEmpty(text))
                Log(Colors.Black, text);
        }
        public static void Logi(IntPtr obj)
        {
            //logr.Debug(text.ToString());
            try
            {
                Log(Colors.Black, "{0:X}", (int)obj);
            }
            catch (System.Exception)
            {

            }

        }
        public static void Log(object text, params object[] arg)
        {
            //logr.Debug(text.ToString());
            try
            {
                Log(Colors.Black, text.ToString());
            }
            catch (System.Exception)
            {

            }

        }

        public static void ClearLog()
        {
            if (MainForm.Instance.textOutput.InvokeRequired)
                MainForm.Instance.textOutput.BeginInvoke(new Action(() => MainForm.Instance.textOutput.Clear()));
            else
                MainForm.Instance.textOutput.Clear();
        }


        public static void Log(Color c, string text, params object[] arg)
        {
            try
            {
                if (string.IsNullOrEmpty(text))
                    return;
                text = string.Format(text, arg);
                if (MainForm.IsValid)
                {
                    try
                    {
                        if (MainForm.Instance.textOutput.InvokeRequired)
                            MainForm.Instance.textOutput.BeginInvoke(new UpdateLogCallback(UpdateLog), c, text);
                        else
                        {
                            MainForm.Instance.textOutput.SelectionStart = MainForm.Instance.textOutput.TextLength;
                            MainForm.Instance.textOutput.SelectionColor = System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B);
                            MainForm.Instance.textOutput.SelectedText = text + Environment.NewLine;
                            MainForm.Instance.textOutput.ScrollToCaret();
                        }
                    }
                    // ReSharper disable EmptyGeneralCatchClause
                    catch
                    {
                    }
                    // ReSharper restore EmptyGeneralCatchClause
                }
                else
                {
                    Logging.Write(text, arg);
                }
            }
            catch (Exception e)
            {
                Logging.WriteException(e);
                //logr.Error(e);
                //logr.DebugFormat(text,arg);
            }
        }

        internal static void UpdateLog(Color c, string text)
        {
            MainForm.Instance.textOutput.SelectionStart = MainForm.Instance.textOutput.TextLength;
            MainForm.Instance.textOutput.SelectionColor = System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B);
            MainForm.Instance.textOutput.SelectedText = text + Environment.NewLine;
            MainForm.Instance.textOutput.ScrollToCaret();
        }

        #region Nested type: UpdateLogCallback

        internal delegate void UpdateLogCallback(Color color, string text);

        #endregion


    }


    public class AppDomainDriver
    {
        public bool CompileAndRun(string code)
        { 
            try
            {
                var codeDriver = (CodeDriver)Activator.CreateInstance(typeof(CodeDriver));
                Pulsator.Pulse(PulseFlags.All);
                //FateManager.Update();
                return codeDriver.CompileAndRun(code);
            }
            catch (Exception ex)
            {
                RebornConsole.Log(Colors.Red, ex.ToString());
                Logging.Write(ex);
                return false;
            }
        }
    }


    public class CodeDriver
    {
        #region Strings

        private const string Prefix = @"
             using System;   
             using System.Reflection;   
             using System.Data;   
             using System.Threading;   
             using System.Diagnostics;   
             using System.Drawing;   
             using System.Collections.Generic;   
             using System.Collections;    
             using System.Linq;    
             using System.Text;    
             using System.IO;    
             using System.Windows.Forms;   
             using System.Text.RegularExpressions;   
             using System.Globalization;   
             using System.Xml.Linq;  
             using System.Runtime.InteropServices; 
             using System.Reflection.Emit;

            using ff14bot;
            using ff14bot.Objects;
            using ff14bot.Managers;
            using ff14bot.NeoProfiles;
            using ff14bot.RemoteWindows;
            using ff14bot.Navigation;
            using ff14bot.Enums;
            using Clio.Utilities;
            using ff14bot.BotBases;
			using ff14bot.Helpers;

             public static class Driver   
             {   
                 
public static void Run()
{
";

        private const string Postfix = @"
              } 

             static public void Log ( string format, params object[] arg) 
             { 
                HighVoltz.RebornConsole.Log(format,arg); 
             } 

             static public void Log ( IntPtr format) 
             { 
                HighVoltz.RebornConsole.Logi(format); 
             } 
             static public void Log ( object format) 
             { 
                HighVoltz.RebornConsole.Log(format); 
             } 
             static public void Api ( string format) 
             { 
                HighVoltz.RebornConsole.Api(format); 
             } 
             static public void ClearLog () 
             { 
                HighVoltz.RebornConsole.ClearLog(); 
             } 



             }
";
        /*              static List<WoWGameObject> GameObjects {get{return ObjectManager.GetObjectsOfType<WoWGameObject>();}} 
                         static List<WoWObject> Objects {get{return ObjectManager.ObjectList;}} 
                         static List<WoWUnit> Units {get{return ObjectManager.GetObjectsOfType<WoWUnit>();}} 
                         static List<WoWPlayer> Players {get{return ObjectManager.GetObjectsOfType<WoWPlayer>();}} 
                         static List<WoWItem> Items {get{return ObjectManager.GetObjectsOfType<WoWItem>();}} 
                         static List<WoWDynamicObject> DynamicObjects {get{return ObjectManager.GetObjectsOfType<WoWDynamicObject>();}} */

        #endregion

        // returns true if compiled sucessfully
        // returns true if compiled sucessfully

        public bool CompileAndRun(string input)
        {
            CompilerResults results;
            using (var provider = new CSharpCodeProvider(new Dictionary<string, string>
                                                             {
                                                                 {"CompilerVersion", "v4.0"},
                                                             }))
            {
                var options = new CompilerParameters();
                // most recent assembly



                var myAssembly = AppDomain.CurrentDomain.GetAssemblies()
                    .Where(a => a.GetName().Name.Contains(RebornConsole.Instance.Name))
                    .OrderByDescending(a => new FileInfo(a.Location).CreationTime).FirstOrDefault();

                options.ReferencedAssemblies.Add(myAssembly.Location);
                foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
                {
                    if (!asm.GetName().Name.Contains(RebornConsole.Instance.Name) && !asm.IsDynamic)
                        options.ReferencedAssemblies.Add(asm.Location);
                }

                options.GenerateExecutable = false;
                options.TempFiles = new TempFileCollection(RebornConsole.TempFolder, false);
                options.IncludeDebugInformation = true;
                options.OutputAssembly = string.Format("{0}\\CodeAssembly{1:N}.dll", RebornConsole.TempFolder, Guid.NewGuid());
                options.CompilerOptions = "/d:DEBUG /optimize /unsafe";

                var sb = new StringBuilder();
                sb.Append(Prefix);
                sb.Append(input);
                sb.Append(Postfix);
                results = provider.CompileAssemblyFromSource(options, sb.ToString());
            }
            if (results.Errors.HasErrors)
            {
                var errorMessage = new StringBuilder();
                var errLineOffset = Prefix.Count(c => c == '\n'); ;
                foreach (CompilerError error in results.Errors)
                {
                    errorMessage.AppendFormat("{0}) {1}\n", error.Line - errLineOffset,
                                              error.ErrorText);
                }
                RebornConsole.Log(Colors.Red, errorMessage.ToString());
                return false;
            }
            Type driverType = results.CompiledAssembly.GetType("Driver");
            try
            {
                driverType.InvokeMember("Run", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, null);
            }
            catch (Exception e)
            {
                RebornConsole.Log(e);
            }

            return true;
        }
    }


    internal class RebornConsoleSettings : JsonSettings
    {
        internal static readonly RebornConsoleSettings Instance = new RebornConsoleSettings();

        public RebornConsoleSettings()
            : base(GetSettingsFilePath("Global", "RebornConsole.json"))
        {
            if (CSharpSniplets == null)
                CSharpSniplets = new[] { "" };

            if (CSharpSnipletNames == null)
                CSharpSnipletNames = new[] { "Untitled1" };

            //CSharpSelectedIndex = 0;
            if (Keybind == Keys.None)
                Keybind = Keys.F4;
        }

        [Setting]
        public string[] CSharpSniplets { get; set; }

        [Setting]
        public string[] CSharpSnipletNames { get; set; }

        [Setting]
        public int CSharpSelectedIndex { get; set; }

        [Setting]
        [DefaultValue(null)]
        public Keys Keybind { get; set; }
    }
}