namespace Dreamteck.Splines.Editor
{
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    using System.IO;
    using Dreamteck.Splines.IO;

    public class ImportExportTool : SplineTool
    {
        private float scaleFactor = 1f;
        private bool alwaysDraw = true;
        private string importPath = "";
        private string exportPath = "";
        List<SplinePoint[]> originalPoints = new List<SplinePoint[]>();
        List<SplineComputer> imported = new List<SplineComputer>();
        List<SplineComputer> exported = new List<SplineComputer>();
        GameObject importedParent = null;

        enum Mode { None, Import, Export }
        enum ExportFormat { SVG, CSV }
        enum Axis { X, Y, Z }

        Mode mode = Mode.None;
        ExportFormat format = ExportFormat.SVG;
        Axis importAxis = Axis.Z;
        Axis exportAxis = Axis.Z;

        bool importOptions = false;

        List<CSV.ColumnType> exportColumns = new List<CSV.ColumnType>();
        List<CSV.ColumnType> importColumns = new List<CSV.ColumnType>();
        bool flatCSV = false;

        public override string GetName()
        {
            return "Import/Export";
        }

        protected override string GetPrefix()
        {
            return "ImportExport";
        }

        public override void Open(EditorWindow window)
        {
            base.Open(window);
            importPath = LoadString("importPath", "");
            exportPath = LoadString("exportPath", "");
            alwaysDraw = LoadBool("alwaysDraw", true);
            flatCSV = LoadBool("flatCSV", false);
            importAxis = (Axis)LoadInt("importAxis", 2);
            exportAxis = (Axis)LoadInt("exportAxis", 2);
            LoadColumns("importColumns", ref importColumns);
            LoadColumns("exportColumns", ref exportColumns);
        }

        void LoadColumns(string name, ref List<CSV.ColumnType> destination)
        {
            string text = LoadString(name, "");
            destination = new List<CSV.ColumnType>();
            if (text == "")
            {
                destination.Add(CSV.ColumnType.Position);
                destination.Add(CSV.ColumnType.Tangent);
                destination.Add(CSV.ColumnType.Tangent2);
                destination.Add(CSV.ColumnType.Normal);
                destination.Add(CSV.ColumnType.Size);
                destination.Add(CSV.ColumnType.Color);
                return;
            }
            string[] elements = text.Split(',');
            foreach (string element in elements)
            {
                int i = 0;
                if (int.TryParse(element, out i)) destination.Add((CSV.ColumnType)i);
            } 
        }

        public override void Close()
        {
            base.Close(); 
            if(importPath != "") SaveString("importPath", Path.GetDirectoryName(importPath));
            if (exportPath != "")  SaveString("exportPath", Path.GetDirectoryName(exportPath));
            string columnString = ""; 
            foreach(CSV.ColumnType col in importColumns)
            {
                if (columnString != "") columnString += ",";
                columnString += ((int)col).ToString();
            }
            SaveString("importColumns", columnString);
            columnString = "";
            foreach (CSV.ColumnType col in exportColumns)
            {
                if (columnString != "") columnString += ",";
                columnString += ((int)col).ToString();
            }
            SaveString("exportColumns", columnString);
            SaveBool("alwaysDraw", alwaysDraw);
            SaveBool("flatCSV", flatCSV);
            SaveInt("importAxis", (int)importAxis);
            SaveInt("exportAxis", (int)exportAxis);

#if UNITY_2019_1_OR_NEWER
            SceneView.duringSceneGui -= OnScene;
#else
            SceneView.onSceneGUIDelegate -= OnScene;
#endif

        } 

        protected override void Save()
        {
            base.Save();
            if (importedParent != null)
            {
                Selection.activeGameObject = importedParent;
                importedParent = null;
            } else
            {
                foreach(SplineComputer comp in imported)
                {
                    if(comp != null)
                    {
                        Selection.activeGameObject = comp.gameObject;
                        break;
                    }
                }
            }
            imported.Clear();
#if UNITY_2019_1_OR_NEWER
            SceneView.duringSceneGui -= OnScene;
#else
            SceneView.onSceneGUIDelegate -= OnScene;
#endif

            mode = Mode.None;
        }

        protected override void Cancel()
        {
            base.Cancel();
            foreach (SplineComputer spline in imported) GameObject.DestroyImmediate(spline.gameObject);
            GameObject.DestroyImmediate(importedParent);
            imported.Clear();
            SceneView.RepaintAll();
#if UNITY_2019_1_OR_NEWER
            SceneView.duringSceneGui -= OnScene;
#else
            SceneView.onSceneGUIDelegate -= OnScene;
#endif

            mode = Mode.None;
        }

        void CSVColumnUI(List<CSV.ColumnType> columns)
        {
            EditorGUILayout.LabelField("Dataset Columns");
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("-", GUILayout.MaxWidth(30)) && columns.Count > 0) columns.RemoveAt(columns.Count - 1);
            for (int i = 0; i < columns.Count; i++)
            {
                columns[i] = (CSV.ColumnType)EditorGUILayout.EnumPopup(columns[i]);
            }
            if (GUILayout.Button("+", GUILayout.MaxWidth(30)) && columns.Count > 0) columns.Add(CSV.ColumnType.Position);
            EditorGUILayout.EndHorizontal();
        }

        void OnScene(SceneView current)
        {
            for (int i = 0; i < imported.Count; i++)
            {
                DSSplineDrawer.DrawSplineComputer(imported[i]);
            }
        }

        void ImportUI()
        {
            EditorGUI.BeginChangeCheck();
            scaleFactor = EditorGUILayout.FloatField("Scale Factor", scaleFactor);
            importAxis = (Axis)EditorGUILayout.EnumPopup("Facing Axis", importAxis);
            alwaysDraw = EditorGUILayout.Toggle("Always Draw", alwaysDraw);
            if (EditorGUI.EndChangeCheck()) ApplyPoints();
            SaveCancelUI();
        }

        void ExportUI()
        {
            if(exported.Count == 0)
            {
                mode = Mode.None;
                return;
            }
            EditorGUILayout.Space();
            format = (ExportFormat)EditorGUILayout.EnumPopup("Format", format);
            if (format == ExportFormat.SVG)
            {
                exportAxis = (Axis)EditorGUILayout.EnumPopup("Projection Axis", exportAxis);
                EditorGUILayout.HelpBox("The SVG is a 2D vector format so the exported spline will be flattened along the selected axis", MessageType.Info);
            }
            else
            {
                CSVColumnUI(exportColumns);
                flatCSV = EditorGUILayout.Toggle("Flat", flatCSV);
                if(flatCSV) exportAxis = (Axis)EditorGUILayout.EnumPopup("Projection Axis", exportAxis);
                EditorGUILayout.HelpBox("The exported splined will be flattened along the selected axis.", MessageType.Info);

            }

            if (GUILayout.Button("Save File"))
            {
                string extension = "*";
                switch (format)
                {
                    case ExportFormat.SVG: extension = "svg"; break;
                    case ExportFormat.CSV: extension = "csv"; break;
                }
                exportPath = EditorUtility.SaveFilePanel("Export splines", exportPath, "spline", extension);
                if (exportPath != "")
                {
                    if (Directory.Exists(Path.GetDirectoryName(exportPath)))
                    {
                        switch (format)
                        {
                            case ExportFormat.SVG: ExportSVG(exportPath); break;
                            case ExportFormat.CSV: ExportCSV(exportPath); break;
                        }
                    }
                }
            }
        }

        public override void Draw(Rect windowRect)
        {
            if (mode == Mode.Import)
            {
                ImportUI();
            } 
            else
            {
                importOptions = EditorGUILayout.Foldout(importOptions, "Import Options");
                if (importOptions) CSVColumnUI(importColumns);
                if (GUILayout.Button("Import"))
                {
                    importPath = EditorUtility.OpenFilePanel("Browse File", importPath, "svg,csv");
                    if (File.Exists(importPath))
                    {
                        splines.Clear();
                        string ext = Path.GetExtension(importPath).ToLower();
                        switch (ext)
                        {
                            case ".svg": ImportSVG(importPath); break;
                            case ".csv": ImportCSV(importPath); break;
                            case ".xml": ImportSVG(importPath); break;
                        }
                    }
                }
                exported = GetSelectedSplines();
                if (exported.Count == 0) GUI.color = new Color(1f, 1f, 1f, 0.5f);
                if (mode == Mode.Export) ExportUI();
                if (mode != Mode.Export)
                {
                    if (GUILayout.Button("Export") && exported.Count > 0) mode = Mode.Export;
                }
            }
        }

        List<SplineComputer> GetSelectedSplines()
        {
            List<SplineComputer> selected = new List<SplineComputer>();
            foreach(GameObject obj in Selection.gameObjects)
            {
                SplineComputer comp = obj.GetComponent<SplineComputer>();
                if (comp != null) selected.Add(comp);
            }
            return selected;
        }

        void ApplyPoints()
        {
            if (originalPoints.Count != imported.Count) return;
            if (imported.Count == 0) return;
            Quaternion lookRot = Quaternion.identity;
            switch (importAxis)
            {
                case Axis.X: lookRot = Quaternion.LookRotation(Vector3.right); break;
                case Axis.Y: lookRot = Quaternion.LookRotation(Vector3.down); break;
                case Axis.Z: lookRot = Quaternion.LookRotation(Vector3.forward); break;
            }
            for (int i = 0; i < imported.Count; i++)
            {
                SplinePoint[] transformed = new SplinePoint[originalPoints[i].Length];
                originalPoints[i].CopyTo(transformed, 0);
                for (int j = 0; j < transformed.Length; j++)
                {
                    transformed[j].position *= scaleFactor;
                    transformed[j].tangent *= scaleFactor;
                    transformed[j].tangent2 *= scaleFactor;

                    transformed[j].position = lookRot * transformed[j].position;
                    transformed[j].tangent = lookRot * transformed[j].tangent;
                    transformed[j].tangent2 = lookRot * transformed[j].tangent2;
                    transformed[j].normal = lookRot * transformed[j].normal;
                }
                imported[i].SetPoints(transformed);
                if (alwaysDraw)
                {
                    DSSplineDrawer.RegisterComputer(imported[i]);
                }
                else
                {
                    DSSplineDrawer.UnregisterComputer(imported[i]);
                }
            }
            SceneView.RepaintAll();
        }

        void GetImportedPoints()
        {
            foreach (SplineComputer comp in imported)
            {
                if (comp != null)
                {
                    originalPoints.Add(comp.GetPoints(SplineComputer.Space.Local));
                    mode = Mode.Import;
                } else imported.Remove(comp);
            }
        }

        void ImportSVG(string file)
        {
            SVG svg = new SVG(file);
            originalPoints.Clear();
            imported = svg.CreateSplineComputers(Vector3.zero, Quaternion.identity);
            if (imported.Count == 0) return;
            importedParent = new GameObject(svg.name);
            foreach (SplineComputer comp in imported) comp.transform.parent = importedParent.transform;
#if UNITY_2019_1_OR_NEWER
            SceneView.duringSceneGui += OnScene;
#else
            SceneView.onSceneGUIDelegate += OnScene;
#endif

            GetImportedPoints();
            ApplyPoints();
            promptSave = true;
        }

        void ExportSVG(string file)
        {
            SVG svg = new SVG(exported);
            svg.Write(file, (SVG.Axis)((int)exportAxis));
        }

        void ExportCSV(string file)
        {
            CSV csv = new CSV(exported[0]);
            csv.columns = exportColumns;
            if (flatCSV)
            { 
                switch (exportAxis)
                {
                    case Axis.X: csv.FlatX(); break;
                    case Axis.Y: csv.FlatY(); break;
                    case Axis.Z: csv.FlatZ(); break;
                }
            }
            csv.Write(file);
        }


        void ImportCSV(string file)
        {
            CSV csv = new CSV(file, importColumns);
            originalPoints.Clear();
            imported.Clear();
            imported.Add(csv.CreateSplineComputer(Vector3.zero, Quaternion.identity));
            if (imported.Count == 0) return;
#if UNITY_2019_1_OR_NEWER
            SceneView.duringSceneGui += OnScene;
#else
            SceneView.onSceneGUIDelegate += OnScene;
#endif

            GetImportedPoints();
            ApplyPoints();
            promptSave = true;
        }
    }
}