using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Printing;
using System.Reflection;
using System.Threading;

using NRefactory = ICSharpCode.NRefactory;
using Dom = ICSharpCode.SharpDevelop.Dom;
using CodeComplete;
using System.Reflection;
using System.CodeDom.Compiler;
using System.CodeDom;

using DarkWynter.Engine.Globals;
using DarkWynter.App;

namespace CSTextEditor
{
    public partial class PrettyCompilerControl : Form
    {
        internal Dom.ProjectContentRegistry pcRegistry;
        internal Dom.DefaultProjectContent myProjectContent;
        internal Dom.ICompilationUnit lastCompilationUnit;
        CompilerResults compilerResults;

        // Handles Intellisense
        Thread parserThread;

        /// <summary>
        /// Many SharpDevelop.Dom methods take a file name, which is really just a unique identifier
        /// for a file - Dom methods don't try to access code files on disk, so the file does not have
        /// to exist.
        /// SharpDevelop itself uses internal names of the kind "[randomId]/Class1.cs" to support
        /// code-completion in unsaved files.
        /// </summary>
        public const string CompilableFileName = "edited.cs";


        private PrintDocument printDoc = new PrintDocument();
        bool changed = false;

        public delegate void TextChanged_EventHandler(string message);
        TextChanged_EventHandler textChanged_EventHandler;

        public delegate void Compile_EventHandler(ref TextBox output);
        public delegate void Run_EventHandler(ref TextBox output);

        public PrettyCompilerControl()
        {
            // Code Project
            myProjectContent = new Dom.DefaultProjectContent();
            myProjectContent.Language = Dom.LanguageProperties.CSharp;

            // Default .NET 2.0 registry
            pcRegistry = new Dom.ProjectContentRegistry(); 

            InitializeComponent();

            textChanged_EventHandler += new TextChanged_EventHandler(CodeBaseChanged);
            printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);

            textEditorControl1.SetHighlighting("C#");
            textEditorControl1.Document.DocumentChanged += new ICSharpCode.TextEditor.Document.DocumentEventHandler(textChanged);
            textEditorControl1.Document.TextEditorProperties.Font = new Font("Courier New", 10);
            //fontDialog1.Font = textEditorControl1.Document.TextEditorProperties.Font;

            textEditorControl1.ShowEOLMarkers = false;
            textEditorControl1.Text =
                "using System;                             \n" +
                "using System.Collections.Generic;          \n" +
                "class MainClass                            \n" +
                "{                                          \n" +
                "    static void Main(string[] args)        \n" +
                "    {                                      \n" +
                "		                                    \n" +
                "    }                                      \n" +
                "}                                          \n";


            CodeCompletionKeyHandler.Attach(this, textEditorControl1);
            HostCallbackImplementation.Register(this);


            // Persistence caches referenced project contents for faster loading.
            // It also activates loading XML documentation files and caching them
            // for faster loading and lower memory usage.
            string path = Path.Combine(Path.GetTempPath(), "CSharpCodeCompletion");
            pcRegistry.ActivatePersistence(path);

            parserThread = new Thread(ParserThread);
            parserThread.IsBackground = true;
            parserThread.Start();


        }

        #region Intellisense
        void ParserThread()
        {
            Thread.Sleep(2000);

            BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Loading mscorlib..."; }));
            myProjectContent.AddReferencedContent(pcRegistry.Mscorlib);

            // do one initial parser step to enable code-completion while other
            // references are loading
            ParseStep();

            string[] referencedAssemblies = {
				"System", "System.Data", "System.Drawing", "System.Xml", "System.Windows.Forms"
			};
            foreach (string assemblyName in referencedAssemblies)
            {
                { // block for anonymous method
                    string assemblyNameCopy = assemblyName;
                    BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Loading " + assemblyNameCopy + "..."; }));
                }
                myProjectContent.AddReferencedContent(pcRegistry.GetProjectContentForReference(assemblyName, assemblyName));
            }
            BeginInvoke(new MethodInvoker(delegate { parserThreadLabel.Text = "Ready"; }));

            // Parse the current file every 2 seconds
            while (!IsDisposed)
            {
                ParseStep();

                Thread.Sleep(2000);
            }
        }

        void ParseStep()
        {
            string code = "";
            Invoke(new MethodInvoker(delegate
            {
                code = textEditorControl1.Text;
            }));
            TextReader textReader = new StringReader(code);
            Dom.ICompilationUnit newCompilationUnit;
            using (NRefactory.IParser p = NRefactory.ParserFactory.CreateParser(NRefactory.SupportedLanguage.CSharp, textReader))
            {
                p.Parse();
                newCompilationUnit = ConvertCompilationUnit(p.CompilationUnit);
            }
            // Remove information from lastCompilationUnit and add information from newCompilationUnit.
            myProjectContent.UpdateCompilationUnit(lastCompilationUnit, newCompilationUnit, PrettyCompilerControl.CompilableFileName);
            lastCompilationUnit = newCompilationUnit;
        }

        Dom.ICompilationUnit ConvertCompilationUnit(NRefactory.Ast.CompilationUnit cu)
        {
            Dom.NRefactoryResolver.NRefactoryASTConvertVisitor converter;
            converter = new Dom.NRefactoryResolver.NRefactoryASTConvertVisitor(myProjectContent);
            cu.AcceptVisitor(converter, null);
            return converter.Cu;
        }
        #endregion

        #region Handled Events
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            compilerOutput.Select(compilerOutput.Text.Length, 0);
            compilerOutput.ScrollToCaret();
        }

        private void OutputBoxChanged(string message)
        {
            compilerOutput.Text = message;
        }

        void textChanged(object sender, EventArgs args)
        {
            CodeBaseChanged(textEditorControl1.Text);
        }
        private void CodeBaseChanged(string message)
        {
            //CompilerControl.richTextBox_CodeWindow.Text = message;
            SynchronizationContext synchContext = SynchronizationContext.Current;


            if (DarkWynter.App.CompilerControl.richTextBox_CodeWindow.InvokeRequired)
            {
                CompilerControl.richTextBox_CodeWindow.Invoke(
                    textChanged_EventHandler,                       // the method to call back on
                    new object[] { message });                      // the list of arguments to pass
            }
            else
            {
                changed = true;
                CompilerControl.richTextBox_CodeWindow.Text = message;
            }

        }

        private void printDoc_PrintPage(Object sender, PrintPageEventArgs e)
        {
            String textToPrint = textEditorControl1.Document.TextContent;
            Font printFont = new Font("Courier New", 12);
            e.Graphics.DrawString(textToPrint, printFont, Brushes.Black, 0, 0);
        }
        #endregion

        #region Compile and Debug
        public virtual void compileToolStripMenuItem_Click(object sender, EventArgs e)
        {        
            //try
            //{
            //    //CSCompiler temp = new CSCompiler(textEditorControl1.Document.TextContent, 
            //    //                new string[] { "System.dll", "System.Windows.Forms.dll" });
            //    //temp.Compile(new CompilerOutputDelegate(this.HandleCompilerOutput));

            //}
            //catch (Exception exp)
            //{
            //    MessageBox.Show(this, "An error occurred compiling this script:\n\n" + exp.Message, "Error compiling script", MessageBoxButtons.OK, MessageBoxIcon.Error);
            //    System.Diagnostics.Debug.WriteLine(exp);
            //}
            Compile(ref compilerOutput);

        }

        private void Compile(ref TextBox output)
        {
            if (DarkWynter.App.CompilerControl.compilerControl.InvokeRequired)
            {
                Compile_EventHandler d = new Compile_EventHandler(Compile);
                DarkWynter.App.CompilerControl.compilerControl.Invoke(d, new object[] { output });
            }
            else
            {
                output.Text = CompilerControl.compilerControl.CompileCodeWithOutput();
            }
        }

        private delegate void AddCompilerOutputLineDelegate(string line);
        public void HandleCompilerOutput(string line)
        {
            compilerOutput.BeginInvoke(new AddCompilerOutputLineDelegate(this.AddCompilerOutputLine), line);
        }
        
        private void AddCompilerOutputLine(string line)
        {
            this.compilerOutput.Text += line + "\r\n";
        }

        public virtual void runCodeStripMenuItem_Click(object sender, EventArgs e)
        {
        //    try
        //    {
        //        string[] temp = new string[1];
        //        //Mike: This is where the program passes its self into the program.
        //        //Here we pass the parameters into the game
        //        //We can put Machina or normal code here.

        //        //Real code
        //        CSCompiler temp2 = new CSCompiler(textEditorControl1.Document.TextContent,
        //                        new string[] { "System.dll", "System.Windows.Forms.dll" });



        //        temp2.Run(
        //            new CompilerOutputDelegate(this.HandleCompilerOutput), 
        //            null, new object[] { temp }, 
        //            CSTextEditor.CSCompiler.GetDefaultScriptPermissionSet());
        //        //Machina Script - Machina script may hide the class Script part (user can add 'using' statements.
        //        //scriptEditorControl1.Script.Run(new CompilerOutputDelegate(this.HandleCompilerOutput), null, new object[] { new Machina() }, scriptPermissions);
        //    }
        //    catch (Exception exp)
        //    {
        //        MessageBox.Show(this, "An error occurred executing this script, please refer to the debug output for details.\n\n" + (exp.InnerException == null ? exp.Message : exp.InnerException.Message) + "", "Error running script", MessageBoxButtons.OK, MessageBoxIcon.Error);
        //        System.Diagnostics.Debug.WriteLine(exp);
        //    }
            
        }
        #endregion

        #region MenuStrip Buttons and Commands

        private void fontToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() != DialogResult.Cancel)
            {
                textEditorControl1.Document.TextEditorProperties.Font = fontDialog1.Font;
                textEditorControl1.Refresh();
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            newFile();
        }

        public virtual void newFile()
        {
            if (!changed || MessageBox.Show(this, "The script source code has been changed. Are you sure you wish to continue?", "Source code changed", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                textEditorControl1.ResetText();
                textEditorControl1.Refresh();
            }
            saveFileDialog1.FileName = "";
            textEditorControl1.FileName = "newFile";
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFile();
        }

        private void openFile()
        {
            openFileDialog1.Filter = "C# Files|*.cs";
            if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
            {
                textEditorControl1.LoadFile(openFileDialog1.FileName);
                textEditorControl1.FileName = openFileDialog1.FileName;
            }
            //So the new file will not save to the old location.
            saveFileDialog1.FileName = openFileDialog1.FileName;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            saveFile();
        }

        private void saveFile()
        {
            if (saveFileDialog1.FileName != "")
            {
                textEditorControl1.SaveFile(saveFileDialog1.FileName);
                textEditorControl1.FileName = saveFileDialog1.FileName;
            }
            else
                saveAs();;
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            saveAs();
        }

        private void saveAs()
        {
            saveFileDialog1.Title = "Specify Destination Filename";
            saveFileDialog1.Filter = "C# Files |*.cs";
            saveFileDialog1.FilterIndex = 1;
            saveFileDialog1.OverwritePrompt = true;

            if (saveFileDialog1.ShowDialog() != DialogResult.Cancel)
            {
                textEditorControl1.SaveFile(saveFileDialog1.FileName);
                textEditorControl1.FileName = saveFileDialog1.FileName;
            }
        }

        private void printToolStripMenuItem_Click(object sender, EventArgs e)
        {
            printFile();
        }

        private void printFile()
        {
            PrintDialog dlg = new PrintDialog();
            dlg.Document = printDoc;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                printDoc.Print();
            }
        }

        private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            printPreview();
        }

        private void printPreview()
        {
            PrintPreviewDialog dlg = new PrintPreviewDialog();
            dlg.Document = printDoc;
            dlg.ShowDialog();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            exit();
        }

        private void exit()
        {
            if (!changed || MessageBox.Show(this, "The script source code has been changed. Are you sure you wish to close?", "Source code changed", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                //this.Close();
                Statics_Engine.SystemSettings.enableCompilerConsole = false;

            }
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textEditorControl1.Undo();
        }

        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textEditorControl1.Redo();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textCut(sender,e);
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textCopy(sender,e);
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textPaste(sender, e);
        }

        private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(sender, e);
        }
        #endregion

        #region Tool Strip
        private void newToolStripButton_Click(object sender, EventArgs e)
        {
            newFile();
        }

        private void openToolStripButton_Click(object sender, EventArgs e)
        {
            openFile();
        }

        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            saveFile();
        }

        private void printToolStripButton_Click(object sender, EventArgs e)
        {
            printFile();
        }

        private void cutToolStripButton_Click(object sender, EventArgs e)
        {
            textCut(sender, e);       
        }

        private void textCut(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(sender, e);
        }

        private void copyToolStripButton_Click(object sender, EventArgs e)
        {
            textCopy(sender, e);
        }

        private void textCopy(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(sender, e);
        }

        private void pasteToolStripButton_Click(object sender, EventArgs e)
        {
            textPaste(sender, e);
        }

        private void textPaste(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(sender, e);
        }

        private void helpToolStripButton_Click(object sender, EventArgs e)
        {

        }        
        #endregion

        #region Context Menu Strip Items
        private void undoToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            textEditorControl1.Undo();
        }

        private void cutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            textCut(sender, e);
        }

        private void copyToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            textCopy(sender, e);
        }

        private void pasteToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            textPaste(sender, e);
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.Delete(sender, e);
        }

        private void selectAllToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            textEditorControl1.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(sender, e);
        }
        #endregion        
    }
}