using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
using Microsoft.DirectX.DirectSound;
using DXGfxLib;

namespace Editor
{
    public partial class MainForm : Form
    {
        private TDView our3DView = null;
        private TimeLineView ourTimeLineView = null;
        private Mediator mediator = null;

        private string sceneFile = "";
        private string sceneFolder = "";
        private string sceneMediaFolder = "";

        private DXGfxLib.Cursor cursor = null;

        private bool disposed = false;
        
        struct ContainerInfo
        {
            public string fullName;
            public Object obj;
        }

        public MainForm()
        {
            InitializeComponent();

            our3DView = new TDView();
            our3DView.Dock = DockStyle.Fill;
            this.mainPanelTab.Controls[0].Controls.Add(our3DView);

            System.Windows.Forms.Application.Idle += new EventHandler(our3DView.OnApplicationIdle);

            mediator = new Mediator(this);

            our3DView.SetMediator(mediator);

            mediator.OnNewScene += new NewScene(mediator_OnNewScene);
            mediator.OnMeshAdded += new MeshAdded(mediator_OnMeshAdded);
            mediator.OnMeshRemoved += new MeshRemoved(mediator_OnMeshRemoved);
            mediator.OnSelect += new Select(mediator_OnSelect);

            sceneTreeView.AfterSelect += new TreeViewEventHandler(sceneTreeView_AfterSelect);
            sceneTreeView.KeyDown += new KeyEventHandler(sceneTreeView_KeyDown);

            mediator.NewScene("No scene", null);
        }

        void sceneTreeView_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (sceneTreeView.SelectedNode != null)
                {
                    ContainerInfo info = (ContainerInfo)sceneTreeView.SelectedNode.Tag;
                    IDisposable disposableObj = info.obj as IDisposable;
                    disposableObj.Dispose();

                    sceneTreeView.Nodes.Remove(sceneTreeView.SelectedNode);
                }
            }
        }

        void mediator_OnSelect(string name, object obj)
        {
            sceneObjPropGrid.SelectedObject = obj;

            IAmScriptable scriptableObj = obj as IAmScriptable;

            UpdateScriptsUI(scriptableObj);
            UpdateTimeLineUI(scriptableObj);
        }


        void scriptEditorUpdate(string name, object obj)
        {
                SceneObject selectedObj = mediator.selectedNode as SceneObject;

                if (selectedObj == null)
                {
                    throw new NullReferenceException("Somehow the select SceneNode is not a sceneObject, it should be both. There is a problem!");
                }
                else
                {
                    membersTextBox.Text = selectedObj.GetScript().GetVarSrc();
                    evaluateTextBox.Text = selectedObj.GetScript().GetCodeSrc();
                    onClickTextBox.Text = selectedObj.GetScript().GetClickCodeSrc();
                    onCollisionTextBox.Text = selectedObj.GetScript().GetCollisionCodeSrc();
                }
            
        }

        void sceneTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode node = e.Node;

            if (node.Tag == null)
            {
                return;
            }

            ContainerInfo info = (ContainerInfo)node.Tag;

            if (info.obj != null)
            {
                mediator.Select(node.Text, info.obj);
            }
        }

        void mediator_OnMeshRemoved(string fileName, Object obj)
        {

            string name = StringUtil.GetName(fileName);

            TreeNode rootNode = this.sceneTreeView.Nodes[0];
            TreeNode toRemove = null;
            foreach (TreeNode node in rootNode.Nodes)
            {
                if (node.Text == name)
                {
                    toRemove = node;
                }
            }
            if (toRemove != null)
            {
                toRemove.Remove();
            }

            if (sceneObjPropGrid.SelectedObject == obj)
            {
                sceneObjPropGrid.SelectedObject = DXGfxManager.GetGlobalInstance().GetScene();
            }
        }

        void mediator_OnMeshAdded(string fileName, Object obj)
        {
            TreeNode rootNode = this.sceneTreeView.Nodes[0];
            TreeNode newNode = rootNode.Nodes.Add(StringUtil.GetName(fileName));
            ContainerInfo info = new ContainerInfo();
            info.fullName = fileName;
            info.obj = obj;
            newNode.Tag = info;
            sceneObjPropGrid.SelectedObject = obj;
        }

        void mediator_OnNewScene(string fileName, Object obj)
        {
            sceneFile = fileName;
            sceneFolder = StringUtil.GetPath(fileName);
            sceneMediaFolder = sceneFolder + "Media";

            this.sceneTreeView.Nodes.Clear();
            TreeNode newNode = this.sceneTreeView.Nodes.Add(StringUtil.GetName(fileName));
            ContainerInfo info = new ContainerInfo();
            info.fullName = fileName;
            info.obj = obj;
            newNode.Tag = info;
            sceneObjPropGrid.SelectedObject = obj;

            our3DView.outdoorScene = obj as OutDoorScene;
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            bool noFurtherProcessing = false;


               // our3DView.OnMsgProc(m.HWnd,
               //     (Microsoft.Samples.DirectX.UtilityToolkit.NativeMethods.WindowMessage)m.Msg,
               //     m.WParam, m.LParam, ref noFurtherProcessing);

        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (e.KeyCode == Keys.Delete)
            {
                if (sceneTreeView.SelectedNode != null)
                {
                    ContainerInfo info = (ContainerInfo)sceneTreeView.SelectedNode.Tag;
                    IDisposable disposableObj = info.obj as IDisposable;
                    disposableObj.Dispose();

                    sceneTreeView.Nodes.Remove(sceneTreeView.SelectedNode);
                }
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewSceneDialog dialog = new NewSceneDialog();

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                string fileName = dialog.Controls["fileNameTextBox"].Text;
                float size = float.Parse(dialog.Controls["sizeTextBox"].Text);
                int numLevel = int.Parse(dialog.Controls["numlevelTextBox"].Text);

                DXGfxLib.Scene scene = DXGfxManager.GetGlobalInstance().NewScene(fileName, size, numLevel);
                mediator.NewScene(fileName, scene);

                cursor = new DXGfxLib.Cursor();
                cursor.name = "Cursor";
                cursor.fileName = "Cursor";
                scene.Attach(cursor);
                scene.sceneContent.Add(cursor.name, cursor);
                mediator.MeshAdded("Cursor", cursor);

                // Let's set the current folder to the scene file folder.
                Directory.SetCurrentDirectory(StringUtil.GetPath(fileName));

                mediator.Select(cursor.name, cursor);
            }

            our3DView.ReadyToGo = true;
        }

        private void setHeigthMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                throw new Exception("No scene! Operation invalid in this context!");
            }

            if (!Directory.Exists(sceneMediaFolder))
            {
                MessageBox.Show("All media that you want to add to the scene should reside in the Media folder in the scene file directory or in a sub folder of the Media Folder, please create that folder and put the media you plan to add to the scene there.");
            }

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.InitialDirectory = sceneMediaFolder;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (!(StringUtil.GetPathWithoutTrailingSlash(dialog.FileName).Equals(sceneMediaFolder)))
                {
                    MessageBox.Show("The object won't be added to the scene. This editor requires that all media files and there dependencies reside in a folder called 'Media' placed in the same location as the scene file");
                }
                else
                {
                    string relativePath = StringUtil.GetRelativePathWithoutStartingSlash(sceneMediaFolder, dialog.FileName);
                    DXGfxManager.GetGlobalInstance().SetHeightMap(relativePath);
                }
            }
        }

        private void setWaterToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                throw new Exception("No scene! Operation invalid in this context!");
            }

            if (!Directory.Exists(sceneMediaFolder))
            {
                MessageBox.Show("All media that you want to add to the scene should reside in the Media folder in the scene file directory or in a sub folder of the Media Folder, please create that folder and put the media you plan to add to the scene there.");
            }

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.InitialDirectory = sceneMediaFolder;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (!(StringUtil.GetPathWithoutTrailingSlash(dialog.FileName).Equals(sceneMediaFolder)))
                {
                    MessageBox.Show("The object won't be added to the scene. This editor requires that all media files and there dependencies reside in a folder called 'Media' placed in the same location as the scene file");
                }
                else
                {
                    string relativePath = StringUtil.GetRelativePathWithoutStartingSlash(sceneMediaFolder, dialog.FileName);
                    Water2 water = DXGfxManager.GetGlobalInstance().SetWater(relativePath);
                    mediator.MeshAdded("Water", water);
                }
            }
        }

        private void setSkyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                throw new Exception("No scene! Operation invalid in this context!");
            }

            if (!Directory.Exists(sceneMediaFolder))
            {
                MessageBox.Show("All media that you want to add to the scene should reside in the Media folder in the scene file directory or in a sub folder of the Media Folder, please create that folder and put the media you plan to add to the scene there.");
            }

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.InitialDirectory = sceneMediaFolder;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (!(StringUtil.GetPathWithoutTrailingSlash(dialog.FileName).Equals(sceneMediaFolder)))
                {
                    MessageBox.Show("The object won't be added to the scene. This editor requires that all media files and there dependencies reside in a folder called 'Media' placed in the same location as the scene file");
                }
                else
                {
                    string relativePath = StringUtil.GetRelativePathWithoutStartingSlash(sceneMediaFolder, dialog.FileName);
                    SkyDome sky = DXGfxManager.GetGlobalInstance().SetSky(relativePath);
                    mediator.MeshAdded("Sky", sky);
                }
            }
        }

        private void addMeshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                throw new Exception("No scene! Operation invalid in this context!");
            }

            if (!Directory.Exists(sceneMediaFolder))
            {
                MessageBox.Show("All media that you want to add to the scene should reside in the Media folder in the scene file directory or in a sub folder of the Media Folder, please create that folder and put the media you plan to add to the scene there.");
            }

            OpenFileDialog dialog = new OpenFileDialog();
            dialog.InitialDirectory = sceneMediaFolder;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (!(StringUtil.GetPathWithoutTrailingSlash(dialog.FileName).Equals(sceneMediaFolder)))
                {
                    MessageBox.Show("The object won't be added to the scene. This editor requires that all media files and there dependencies reside in a folder called 'Media' placed in the same location as the scene file");
                }
                else
                {
                    string relativePath = StringUtil.GetRelativePathWithoutStartingSlash(sceneMediaFolder, dialog.FileName);
                    SceneObject sceneObj = DXGfxManager.GetGlobalInstance().LoadObject(relativePath);

                    sceneObj.WorldMat = cursor.WorldMat;

                    mediator.MeshAdded(sceneObj.name, sceneObj);
                }
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void playSceneBtn_Click(object sender, EventArgs e)
        {
            // Now if we don't have any avatar, let's make one out of the currently selected scene object.
            if (DXGfxManager.GetGlobalInstance().CurrentAvatar == null)
            {
                SceneObject sceneObj = mediator.selectedNode as SceneObject;
                if (sceneObj == null)
                {
                    sceneObj = (SceneObject)DXGfxManager.GetGlobalInstance().GetObj("Cursor");
                }
                DXGfxManager.GetGlobalInstance().CurrentAvatar = new Avatar(sceneObj);
            }

            GetScriptsReady();

            if (!ScriptManager.GetGlobalInstance().scriptsAreReady)
            {
                ScriptManager.GetGlobalInstance().GetReady();
            }
            this.compilationResultsTextBox.Text = ScriptManager.GetGlobalInstance().lastCompilationErrorMessage;

            if (ScriptManager.GetGlobalInstance().scriptsAreReady)
            {
                our3DView.play = true;
            }
            else
            {
                MessageBox.Show("Scripts could not be compiled! You are still in edit mode! Check Compilation errors for further information and what happened.");
                scriptTabControl.SelectedTab = compilateTabPage;
                scriptTabControl.Refresh();
            }
        }

        private void stopSceneBtn_Click(object sender, EventArgs e)
        {
            our3DView.play = false;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {

            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                MessageBox.Show("There is currently no scene to save.");
            }
            else
            {
                SaveScripts(mediator.selectedScriptableObj);
                DXGfxManager.GetGlobalInstance().Save(sceneFile);
            }
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            our3DView.ReadyToGo = false;

            OpenFileDialog dlg = new OpenFileDialog();

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                DXGfxManager.GetGlobalInstance().Load(dlg.FileName, StringUtil.GetPath(dlg.FileName) + "Media");

                mediator.NewScene(dlg.FileName, DXGfxManager.GetGlobalInstance().GetScene());

                Hashtable sceneContent = DXGfxManager.GetGlobalInstance().GetSceneContent();

                IDictionaryEnumerator sceneEnum = sceneContent.GetEnumerator();

                while (sceneEnum.MoveNext())
                {
                    string name = sceneEnum.Key as string;
                    SceneNode node = sceneEnum.Value as SceneNode;

                    if (node != null)
                    {
                        mediator.MeshAdded((string)sceneEnum.Key, (SceneNode)sceneEnum.Value);
                    }
                }
            }

            our3DView.ReadyToGo = true;
        }

        private void referencesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ReferencesDialog refDialog = new ReferencesDialog();
            string[] references = ScriptManager.GetGlobalInstance().GetReferences();
            refDialog.SetReferences(references);

            if (refDialog.ShowDialog() == DialogResult.OK)
            {
                ScriptManager.GetGlobalInstance().SetReferences(refDialog.GetReferences());
            }
        }

        public string[] GetScriptTexts()
        {
            string[] res = new string[4];

            res[0] = membersTextBox.Text;
            res[1] = evaluateTextBox.Text;
            res[2] = onClickTextBox.Text;
            res[3] = onCollisionTextBox.Text;

            return res;
        }

        //public void SaveScripts(SceneObject sceneObj)
        //{
        //    sceneObj.GetScript().UpdateVarSrc(membersTextBox.Text);
        //    sceneObj.GetScript().UpdateCodeSrc(evaluateTextBox.Text);
        //    sceneObj.GetScript().UpdateClickCodeSrc(onClickTextBox.Text);
        //    sceneObj.GetScript().UpdateCollisionCodeSrc(onCollisionTextBox.Text);
        //}

        //public void SaveScripts(SceneNode sceneNode)
        //{
        //    DXGfxLib.SceneObject sceneObj = sceneNode as DXGfxLib.SceneObject;

        //    if (sceneObj != null)
        //    {
        //        SaveScripts(sceneObj);
        //    }
        //}

        public void SaveScripts(IAmScriptable scriptableObj)
        {
            scriptableObj.GetScript().UpdateVarSrc(membersTextBox.Text);
            scriptableObj.GetScript().UpdateCodeSrc(evaluateTextBox.Text);
            scriptableObj.GetScript().UpdateClickCodeSrc(onClickTextBox.Text);
            scriptableObj.GetScript().UpdateCollisionCodeSrc(onCollisionTextBox.Text);
        }

        public void SaveTimeLine(IAmScriptable scriptableObj)
        {
            if (ourTimeLineView != null)
            {
                ourTimeLineView.UpdateScriptableObjectTimeLine(scriptableObj);
            }
        }

        public void UpdateScriptsUI(IAmScriptable scriptableObj)
        {
            if (scriptableObj != null)
            {
                membersTextBox.Text = scriptableObj.GetScript().GetVarSrc();
                evaluateTextBox.Text = scriptableObj.GetScript().GetCodeSrc();
                onClickTextBox.Text = scriptableObj.GetScript().GetClickCodeSrc();
                onCollisionTextBox.Text = scriptableObj.GetScript().GetCollisionCodeSrc();
            }
            else
            {
                membersTextBox.Text = "The currently selected item is not a scriptable object.";
                evaluateTextBox.Text = "The currently selected item is not a scriptable object.";
                onClickTextBox.Text = "The currently selected item is not a scriptable object.";
                onCollisionTextBox.Text = "The currently selected item is not a scriptable object.";
            }
        }

        public void UpdateTimeLineUI(IAmScriptable scriptableObj)
        {
            if (ourTimeLineView == null)
            {
                ourTimeLineView = new TimeLineView(mediator);
                ourTimeLineView.Dock = DockStyle.Fill;
                timeLineTabPage.Controls.Add(ourTimeLineView);
                SetTimeLineTime();
            }

            if (scriptableObj != null)
            {
                ourTimeLineView.UpdateTimeLineView(scriptableObj.GetTimeLine());
            }
        }

        public void GetScriptsReady()
        {
            SaveScripts(mediator.selectedScriptableObj);
        }

        private void addAreaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                throw new Exception("No scene! Operation invalid in this context!");
            }

            if (!Directory.Exists(sceneMediaFolder))
            {
                MessageBox.Show("All media that you want to add to the scene should reside in the Media folder in the scene file directory or in a sub folder of the Media Folder, please create that folder and put the media you plan to add to the scene there.");
            }

            bool goodName = false;
            string name = "Area";

            while (!goodName)
            {
                NewAreaDialog dlg = new NewAreaDialog();

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    SceneObject sceneObj = DXGfxManager.GetGlobalInstance().GetSceneObject(dlg.Controls["nameTextBox"].Text);

                    if (sceneObj == null)
                    {
                        goodName = true;
                        name = dlg.Controls["nameTextBox"].Text;
                    }
                }
            }

            Area area = new Area();

            area.name = name;
            area.fileName = name;
            DXGfxManager.GetGlobalInstance().GetScene().Attach(area);
            mediator.MeshAdded(area.name, area);
        }

        private void timeLineTabPage_Click(object sender, EventArgs e)
        {

        }

        private void SetTimeLineTime()
        {
            if (ourTimeLineView != null)
            {
                double startTime = 0;
                double endTime = 30;

                bool successFul = true;

                successFul = double.TryParse(startTimeTextBox.Text, out startTime);

                if (!successFul)
                {
                    ResetTimeLineTime();
                    MessageBox.Show("Time values are incorrect they have been reseted!");
                    return;
                }

                successFul = double.TryParse(endTimeTextBox.Text, out endTime);

                if (!successFul)
                {
                    ResetTimeLineTime();
                    MessageBox.Show("Time values are incorrect they have been reseted!");
                    return;
                }
                else
                {
                    ourTimeLineView.SetStartAndEnd(startTime, endTime);
                }

                ourTimeLineView.Refresh();
            }
        }

        private void ResetTimeLineTime()
        {
            if (ourTimeLineView != null)
            {
                double start;
                double end;
                ourTimeLineView.GetStartAndEnd(out start, out end);

                startTimeTextBox.Text = start.ToString();
                endTimeTextBox.Text = end.ToString();
            }
            else
            {
                startTimeTextBox.Text = "0";
                endTimeTextBox.Text = "30";
            }
        }

        private void endTimeTextBox_Validated(object sender, EventArgs e)
        {
            SetTimeLineTime();
        }

        private void startTimeTextBox_Validated(object sender, EventArgs e)
        {
            SetTimeLineTime();
        }

        private void setAvatarToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            SceneObject sceneObj = mediator.selectedNode as SceneObject;

            if (sceneObj != null)
            {
                DXGfxManager.GetGlobalInstance().CurrentAvatar = new Avatar(sceneObj);

                MessageBox.Show(sceneObj.name + " is now the new scene Avatar. It will be controlled by user inputs when the scene will be playing.");
            }
            else
            {
                MessageBox.Show("The currently selected scene node is not a scene object and thus we can not make it the current scene avatar!");
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DXGfxManager.GetGlobalInstance().GetScene() == null)
            {
                MessageBox.Show("There is currently no scene to save.");
            }
            else
            {
                SaveScripts(mediator.selectedScriptableObj);

                SaveFileDialog dlg = new SaveFileDialog();

                // Set the properties on dlg so the user is 
                // prompted to create the file if it doesn't exist 
                // or overwrite the file if it does exist.
                dlg.CreatePrompt = true;
                dlg.OverwritePrompt = true;

                // Set the file name to myText.txt, set the type filter
                // to text files, and set the initial directory to drive C.
                dlg.FileName = "myScene";
                dlg.DefaultExt = "xml";
                dlg.Filter = "Scene files (*.xml)|*.xml";

                // Call ShowDialog and check for a return value of DialogResult.OK,
                // which indicates that the file was saved. 
                DialogResult result = dlg.ShowDialog();
                System.IO.Stream fileStream;

                if (result == DialogResult.OK)

                // Open the file, copy the contents of memoryStream to fileStream,
                // and close fileStream. Set the memoryStream.Position value to 0 to 
                // copy the entire stream. 
                {
                    fileStream = dlg.OpenFile();
                    StreamWriter sw = new StreamWriter(fileStream);
                    sw.Write("Empty. File was created but no scene information was ever persisted here!");
                    fileStream.Close();
                }

                DXGfxManager.GetGlobalInstance().Save(dlg.FileName);
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DXGfxManager.GetGlobalInstance().CloseScene();
            mediator.NewScene("No Scene", null);
        }
    }
}