﻿using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Text.RegularExpressions;

namespace T2
{
    [CustomEditor(typeof(T2MapMono))]
    public class T2MapEditor : Editor
    {
        public Map map;
        private string loadPath;
        public string LoadPath
        {
            get { return loadPath; }
            set
            {
                if (loadPath != value)
                {
                    loadPath = value;
                    EditorPrefs.SetString("T2.mapLoadPath", loadPath);
                }
            }
        }
        private string savePath;
        public string SavePath
        {
            get { return savePath; }
            set
            {
                if (savePath != value)
                {
                    savePath = value;
                    EditorPrefs.SetString("T2.mapSavePath", savePath);
                }
            }
        }
        private string mapName;
        public string MapName
        {
            get { return mapName; }
            set
            {
                if (mapName != value)
                {
                    mapName = value;
                    EditorPrefs.SetString("T2.mapName", mapName);
                }
            }
        }

        private SerializedProperty mapProp;
        private SerializedProperty skyLayers;
        private SerializedProperty actors;
        private int tabIndex;
        private bool searchActorTypeIsOn;
        private bool searchActorTIDIsOn;
        private bool searchActorOriginIsOn;
        private int searchActorType;
        private int searchActorTID;
        private Vector3 searchActorOrigin;
        private int searchRegionIndex;
        private int searchActorFoundIndex = -1;
        private Vector3 pathOrigin;
        private int pathTID;
        private int pathCount;
        private int pathType;
        private Vector3 copyMin;
        private Vector3 copyMax;
        private List<StaticMesh> copyStaticMeshes;
        private List<Vector4> copyRegionVerts;
        private List<Region> copyRegions;
        private List<RegionSet> copyRegionSets;
        private List<ActorInfo> copyActors;
        private bool meshRegionCeiling;
        private bool vertsOnly;
        private float meshRegionScale = 1.0f;
        private float meshRegionYaw;
        private Vector3 copyPosOffset;
        private int copyTIDOffset;
        Dictionary<int, int> regionVertIndexLookup;
        Dictionary<int, int> regionSetsIndexLookup;
        Dictionary<int, int> regionIndexLookup;
        private GameObject meshGO;
        private Vector3 moveMin;
        private Vector3 moveMax;
        private Vector3 moveOffset;
        private bool rejectUseFog;
        private float rejectRadius;
        private Vector3 searchRegionOrigin;

        void OnEnable()
        {
            pathCount = 4;
            rejectUseFog = true;
            copyStaticMeshes = new List<StaticMesh>();
            copyRegionVerts = new List<Vector4>();
            copyRegions = new List<Region>();
            copyRegionSets = new List<RegionSet>();
            copyActors = new List<ActorInfo>();
            regionVertIndexLookup = new Dictionary<int, int>();
            regionSetsIndexLookup = new Dictionary<int, int>();
            regionIndexLookup = new Dictionary<int, int>();
            mapProp = serializedObject.FindProperty("map");
            skyLayers = mapProp.FindPropertyRelative("skyInfos");
            actors = mapProp.FindPropertyRelative("actors");
            LoadPath = EditorPrefs.GetString("T2.mapLoadPath");
            SavePath = EditorPrefs.GetString("T2.mapSavePath");
            MapName = EditorPrefs.GetString("T2.mapName");
        }

        public void ShowCounts()
        {
            Debug.Log("Vertices: " + map.collisionVertices.Length);
            Debug.Log("RegionSets: " + map.collisionRegionSets.Length);
            Debug.Log("Regions: " + map.collisionRegions.Length);
            Debug.Log("GridBounds Sections: " + map.gridBounds.sections.Length);

            //int static
            int nStaticMeshes = 0;
            int nLightMapMeshes = 0;
            for (int i = 0; i < map.gridBounds.sections.Length; i++)
            {
                nStaticMeshes += map.gridBounds.sections[i].staticMeshes.Length;
                nLightMapMeshes += map.gridBounds.sections[i].lightMapMeshes.Length;
            }
            Debug.Log("Static Meshes: " + nStaticMeshes);
            Debug.Log("Actors: " + map.actors.Length);

            int nEnemies = 0;
            for (int i = 0; i < map.actors.Length; i++)
            {
                for (int j = 0; j < map.actors[i].components.Length; j++)
                {
                    if (map.actors[i].components[j].name == "kexEnemyAIComponent")
                    {
                        nEnemies++;
                        break;
                    }
                }
            }
            Debug.Log("Enemies: " + nEnemies);
            Debug.Log("Visibility: " + map.visibility.Length + (map.visibility.Length > 0 ? (" staticmeshesLength: " + map.visibility[0].staticMeshes.Length) : ""));
            Debug.Log("Lightmap Meshes: " + nLightMapMeshes);
            Debug.Log("Rejects: " + map.rejects.Length + (map.rejects.Length > 0 ? (" regionsLength: " + map.rejects[0].regions.Length) : ""));
            Debug.Log("Lightmap Textures: " + map.lightMapTextures.Length);
        }

        public void CheckActorTIDS()
        {
            List<Vector2Int> tids = new List<Vector2Int>();
            for (int i = 0; i < map.actors.Length; i++)
            {
                if (map.actors[i].TID >= 0)
                {
                    tids.Add(new Vector2Int(map.actors[i].TID, 1));
                }
            }
            for (int i = 0; i < map.collisionRegionSets.Length; i++)
            {
                if (map.collisionRegionSets[i].tagID >= 0)
                {
                    int fi = tids.FindIndex(x => x.x == map.collisionRegionSets[i].tagID);
                    if (fi >= 0)
                    {
                        Vector2Int t = tids[fi];
                        t.y |= (1 << 1);
                        tids[fi] = t;
                    }
                    else
                    {
                        tids.Add(new Vector2Int(map.collisionRegionSets[i].tagID, 2));
                    }
                }
            }
            tids.Sort(delegate (Vector2Int x, Vector2Int y) { return x.x.CompareTo(y.x); });

            for (int i = 0; i < tids.Count; i++)
            {
                string s = "";
                if ((tids[i].y & (1 << 0)) != 0)
                    s += " | Actor";
                if ((tids[i].y & (1 << 1)) != 0)
                    s += " | Region";
                Debug.Log(tids[i].x + s);
            }
        }

        public void CheckEventLinkGroupIDs()
        {
            for (int i = 0; i < map.actors.Length; i++)
            {
                for (int j = 0; j < map.actors[i].components.Length; j++)
                {
                    if (map.actors[i].components[j].name == "kexEventLinkComponent")
                    {
                        int groupID = Convert.ToInt32(map.actors[i].components[j].keyValues["groupID"]);
                        if (groupID > 0)
                        {
                            Debug.Log(groupID);
                        }
                    }
                }
            }
        }

        public void ListStaticMeshNames()
        {
            List<string> names = new List<string>();
            for (int i = 0; i < map.gridBounds.sections.Length; i++)
            {
                GridSection section = map.gridBounds.sections[i];
                for (int j = 0; j < section.staticMeshes.Length; j++)
                {
                    if (!names.Contains(section.staticMeshes[j].modelPath))
                    {
                        names.Add(section.staticMeshes[j].modelPath);
                    }
                }
            }
            Debug.Log("" + names.Count + " different types of static meshes");
            for (int i = 0; i < names.Count; i++)
            {
                Debug.Log(names[i]);
            }
        }

        public void Test()
        {
            Bounds worldBounds = new Bounds();
            for (int i = 0; i < map.gridBounds.sections.Length; i++)
            {
                for (int j = 0; j < map.gridBounds.sections[i].staticMeshes.Length; j++)
                {
                    StaticMesh mesh = map.gridBounds.sections[i].staticMeshes[j];
                    Vector3 boundSize = mesh.boundsMax - mesh.boundsMin;
                    Bounds meshBounds = new Bounds(mesh.boundsMin + boundSize / 2, boundSize);
                    worldBounds.Encapsulate(meshBounds);
                }
            }
            //worldBounds.SetMinMax(new Vector3(9.574768f, -154.2652f, 0), new Vector3(1997.465f, 154.2652f, 0));
            MapGridInfo g = new MapGridInfo();
            g.BuildGridInfo(worldBounds, 110.0f * 10.24f);
            Debug.Log("BlockSize: " + g.blockSize.ToString() + " min: " + g.min.ToString("G6") + " max: " + g.max.ToString("G6") + " size: " + g.size.ToString("G6") + " unit: " + g.unit.ToString("G6"));
        }

        public void RemoveLightmaps()
        {
            map.lightMapTextures = new LightMapTexture[0];
            for (int i = 0; i < map.gridBounds.sections.Length; i++)
            {
                map.gridBounds.sections[i].lightMapMeshes = new LightMapMesh[0];
            }
        }

        public override void OnInspectorGUI()
        {
            T2MapMono mb = target as T2MapMono;
            map = mb.map;

            serializedObject.Update();

            EditorGUILayout.Space();
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Load File"))
            {
                string path = EditorUtility.OpenFilePanel("Open T2 Map file", LoadPath, "map");
                if (!string.IsNullOrEmpty(path))
                {
                    LoadPath = Path.GetDirectoryName(path);
                    MapName = Path.GetFileNameWithoutExtension(path);
                    map.Load(path);
                }
            }

            if (GUILayout.Button("Save File"))
            {
                string path = EditorUtility.SaveFilePanel("Save T2 Map file", SavePath, MapName, "map");
                if (!string.IsNullOrEmpty(path))
                {
                    SavePath = Path.GetDirectoryName(path);
                    MapName = Path.GetFileNameWithoutExtension(path);
                    map.Save(path);
                }
            }
            GUILayout.EndHorizontal();

            MapName = EditorGUILayout.TextField("Filename", MapName);


            EditorGUILayout.Space();

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Reload"))
            {
                map.Load(LoadPath + "/" + mapName + ".map");
            }
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUI.BeginDisabledGroup(map.visibility.Length == 0);
            if (GUILayout.Button("Remove Vis"))
            {
                map.visibility = new Visibility[0];
            }
            EditorGUI.EndDisabledGroup();
            EditorGUI.BeginDisabledGroup(map.rejects.Length == 0);
            if (GUILayout.Button("Remove Rejects"))
            {
                map.rejects = new Rejects[0];
            }
            EditorGUI.EndDisabledGroup();
            EditorGUI.BeginDisabledGroup(map.lightMapTextures.Length == 0);
            if (GUILayout.Button("Remove Lightmaps"))
            {
                RemoveLightmaps();
            }
            EditorGUI.EndDisabledGroup();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUI.BeginDisabledGroup(map.lightMapTextures.Length == 0);
            if (GUILayout.Button("Save Lightmaps"))
            {
                string path = EditorUtility.SaveFolderPanel("Lightmap Textures destination folder", "", "");
                if (!string.IsNullOrEmpty(path))
                {
                    for (int i = 0; i < map.lightMapTextures.Length; i++)
                    {
                        map.lightMapTextures[i].CreatePNG(path + "/lightmap" + i + ".png");
                    }
                    AssetDatabase.Refresh();
                }
            }
            EditorGUI.EndDisabledGroup();

            if (GUILayout.Button("Remove Unused Verts"))
            {
                bool[] vertUsed = new bool[map.collisionVertices.Length];
                for (int i = 0; i < map.collisionRegions.Length; i++)
                {
                    vertUsed[map.collisionRegions[i].indice1] = true;
                    vertUsed[map.collisionRegions[i].indice2] = true;
                    vertUsed[map.collisionRegions[i].indice3] = true;
                }

                Dictionary<int, int> vertLookup = new Dictionary<int, int>();
                int vertIndex = 0;
                for (int i = 0; i < vertUsed.Length; i++)
                {
                    if (vertUsed[i])
                    {
                        vertLookup.Add(i, vertIndex);
                        vertIndex++;
                    }
                }

                //Remove unused verts
                int nRemoved = 0;
                for (int i = vertUsed.Length - 1; i >= 0; i--)
                {
                    if (!vertUsed[i])
                    {
						//Debug.Log("x: " + map.collisionVertices[i].x + " y: " + map.collisionVertices[i].y + " z: " + map.collisionVertices[i].z);
                        ArrayUtility.RemoveAt(ref map.collisionVertices, i);
                        nRemoved++;
                    }
                }

                //update region indices
                for (int i = 0; i < map.collisionRegions.Length; i++)
                {
                    map.collisionRegions[i].indice1 = (short)vertLookup[map.collisionRegions[i].indice1];
                    map.collisionRegions[i].indice2 = (short)vertLookup[map.collisionRegions[i].indice2];
                    map.collisionRegions[i].indice3 = (short)vertLookup[map.collisionRegions[i].indice3];
                }

                Debug.Log("Removed " + nRemoved + " unused verts");
            }
            if (GUILayout.Button("Static ms"))
            {
                ListStaticMeshNames();
            }

            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Show Counts"))
            {
                ShowCounts();
            }
            if (GUILayout.Button("TIDs"))
            {
                CheckActorTIDS();
            }
            if (GUILayout.Button("GroupIDs"))
            {
                CheckEventLinkGroupIDs();
            }

            GUILayout.EndHorizontal();
            EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

            tabIndex = GUILayout.SelectionGrid(tabIndex, new string[] { "Find Actor", "Sky Layers", "Add Path", "Copy & Paste", "Mesh to regions", "Offset", "Create Rejects", "Find Region" }, 3);
            switch (tabIndex)
            {
                case 0: //find actor
                {
                    EditorGUI.BeginChangeCheck();
                    EditorGUIUtility.labelWidth = 60.0f;
                    GUILayout.BeginHorizontal();
                    searchActorTypeIsOn = EditorGUILayout.Toggle(searchActorTypeIsOn, GUILayout.Width(20));
                    EditorGUI.BeginDisabledGroup(!searchActorTypeIsOn);
                    searchActorType = EditorGUILayout.IntField("Type", searchActorType);
                    EditorGUI.EndDisabledGroup();
                    GUILayout.EndHorizontal();
                    GUILayout.BeginHorizontal();
                    searchActorTIDIsOn = EditorGUILayout.Toggle(searchActorTIDIsOn, GUILayout.Width(20));
                    EditorGUI.BeginDisabledGroup(!searchActorTIDIsOn);
                    searchActorTID = EditorGUILayout.IntField("TID", searchActorTID);
                    EditorGUI.EndDisabledGroup();
                    GUILayout.EndHorizontal();
                    GUILayout.BeginHorizontal();
                    searchActorOriginIsOn = EditorGUILayout.Toggle(searchActorOriginIsOn, GUILayout.Width(20));
                    EditorGUI.BeginDisabledGroup(!searchActorOriginIsOn);
                    searchActorOrigin = EditorGUILayout.Vector3Field("Origin", searchActorOrigin);
                    EditorGUI.EndDisabledGroup();
                    GUILayout.EndHorizontal();
                    EditorGUIUtility.labelWidth = 0.0f;
                    EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
                    if (EditorGUI.EndChangeCheck())
                    {
                        for (int i = 0; i < map.actors.Length; i++)
                        {
                            ActorInfo a = map.actors[i];
                            bool typeMatch = !searchActorTypeIsOn || (searchActorTypeIsOn && a.actorID == searchActorType);
                            bool tidMatch = !searchActorTIDIsOn || (searchActorTIDIsOn && a.TID == searchActorTID);
                            bool posMatch = !searchActorOriginIsOn || (searchActorOriginIsOn && Vector3.Distance(a.origin, searchActorOrigin) < 1.0f);

                            if (typeMatch && tidMatch && posMatch)
                            {
                                searchActorFoundIndex = i;
                                SerializedProperty spActor = actors.GetArrayElementAtIndex(searchActorFoundIndex);
                                spActor.isExpanded = true;
                                spActor.FindPropertyRelative("components").isExpanded = true;
                                break;
                            }
                        }
                    }

                    if (searchActorFoundIndex >= 0 && searchActorFoundIndex < actors.arraySize)
                    {
                        SerializedProperty spActor = actors.GetArrayElementAtIndex(searchActorFoundIndex);
                        bool pressDelete = GUILayout.Button("Delete this actor");
                        if (pressDelete)
                        {
                            if (EditorUtility.DisplayDialog("Confirm", "Are you sure you want to delete this actor?", "Yes", "No"))
                            {
                                ArrayUtility.Clear(ref map.actors);

                                //ArrayUtility.RemoveAt(ref map.actors, searchActorFoundIndex);
                                searchActorFoundIndex = -1;
                            }
                        }
                        else
                        {
                            EditorGUILayout.PropertyField(spActor, new GUIContent("Actor (Index " + searchActorFoundIndex + ")"), true);
                        }
                    }
                    else
                    {
                        EditorGUILayout.LabelField("No Actor");
                    }
                    break;
                }
                case 1:
                {
                    EditorGUILayout.PropertyField(skyLayers, new GUIContent("Sky Layers"), true);
                    break;
                }
                case 2:
                {
                    pathOrigin = EditorGUILayout.Vector3Field("Spawn Pos", pathOrigin);
                    pathCount = EditorGUILayout.IntField("# of Points", pathCount);
                    pathTID = EditorGUILayout.IntField("Start TID", pathTID);
                    pathType = EditorGUILayout.Popup("Path Type", pathType, new string[] { "Ping Pong", "Loop" });
                    if (GUILayout.Button("Create Path") && pathCount > 1)
                    {
                        ActorInfo pathActor = new ActorInfo();
                        pathActor.actorID = 20003;
                        pathActor.origin = new Vector3(pathOrigin.x, pathOrigin.y, pathOrigin.z + 128.0f);
                        pathActor.TID = pathTID;
                        ActorComponent compSpriteRender = new ActorComponent();
                        compSpriteRender.name = "kexRenderSpriteComponent";
                        ArrayUtility.Add(ref pathActor.components, compSpriteRender);
                        ActorComponent compPath = new ActorComponent();
                        compPath.name = "kexPathComponent";
                        compPath.keyValues.Add("type", "" + pathType);
                        int pointTID = pathTID + 1;
                        for (int i = 0; i < pathCount; i++)
                        {
                            compPath.keyValues.Add("path_" + (i + 1), "" + pointTID);
                            pointTID++;
                        }
                        ArrayUtility.Add(ref pathActor.components, compPath);
                        ArrayUtility.Add(ref map.actors, pathActor);

                        pointTID = pathTID + 1;
                        for (int i = 0; i < pathCount; i++)
                        {
                            ActorInfo pointActor = new ActorInfo();
                            pointActor.actorID = 20002;
                            pointActor.TID = pointTID + i;
                            pointActor.origin = new Vector3(pathOrigin.x + 40.96f * i, pathOrigin.y, pathOrigin.z);
                            ActorComponent pointCompSpriteRender = new ActorComponent();
                            pointCompSpriteRender.name = "kexRenderSpriteComponent";
                            ArrayUtility.Add(ref pointActor.components, pointCompSpriteRender);

                            ActorComponent pointWorld = new ActorComponent();
                            pointWorld.name = "kexWorldComponent";
                            pointWorld.keyValues.Add("heightOffset", "0.000000");
                            pointWorld.keyValues.Add("bTrackGround", "0");
                            pointWorld.keyValues.Add("touchRadius", "0.000000");
                            pointWorld.keyValues.Add("regionID", "0");
                            pointWorld.keyValues.Add("wallRadius", "0.000000");
                            pointWorld.keyValues.Add("radius", "0.000000");
                            pointWorld.keyValues.Add("deadHeight", "0.000000");
                            pointWorld.keyValues.Add("height", "0.000000");
                            pointWorld.keyValues.Add("bNonSolid", "1");
                            ArrayUtility.Add(ref pointActor.components, pointWorld);

                            ArrayUtility.Add(ref map.actors, pointActor);
                        }
                    }
                    break;
                }
                case 3: //copy paste everything in min/max area
                {
                    copyMin = EditorGUILayout.Vector3Field("Copy Min", copyMin);
                    copyMax = EditorGUILayout.Vector3Field("Copy Max", copyMax);
                    bool areaPressed = GUILayout.Button("All Area");
                    if (areaPressed)
                    {
                        copyMin = new Vector3(-20000, -20000, -20000);
                        copyMax = new Vector3(20000, 20000, 20000);
                    }
                    copyPosOffset = EditorGUILayout.Vector3Field("Copy Offset", copyPosOffset);
                    copyTIDOffset = EditorGUILayout.IntField("Copy TID Offset", copyTIDOffset);
                    bool copyPressed = GUILayout.Button("Copy");
                    EditorGUI.BeginDisabledGroup(copyActors.Count == 0 && copyRegions.Count == 0 && copyRegionSets.Count == 0 && copyRegionVerts.Count == 0 && copyStaticMeshes.Count == 0);
                    bool pastePressed = GUILayout.Button("Paste");
                    EditorGUI.EndDisabledGroup();
                    if (copyPressed)
                    {
                        Vector3 boundSize = copyMax - copyMin;
                        Bounds copyBounds = new Bounds(copyMin + boundSize / 2, boundSize);
                        //copyBounds.SetMinMax(copyMax, copyMax);

                        //Get all staticmeshes
                        copyStaticMeshes.Clear();
                        for (int i = 0; i < map.gridBounds.sections.Length; i++)
                        {
                            for (int j = 0; j < map.gridBounds.sections[i].staticMeshes.Length; j++)
                            {
                                StaticMesh mesh = map.gridBounds.sections[i].staticMeshes[j];
                                if (copyBounds.Contains(mesh.origin))
                                {
                                    mesh.boundsMin += copyPosOffset;
                                    mesh.boundsMax += copyPosOffset;
                                    mesh.origin += copyPosOffset;
                                    copyStaticMeshes.Add(mesh);
                                }
                            }
                        }

                        //Get all the regions that are connected to the verts in the bounds
                        copyRegions.Clear();
                        regionIndexLookup.Clear();
                        //List<int> regionIndexes = new List<int>();
                        for (int i = 0; i < map.collisionVertices.Length; i++)
                        {
                            if (copyBounds.Contains(map.collisionVertices[i]))
                            {
                                //Find region with the vertex
                                for (int j = 0; j < map.collisionRegions.Length; j++)
                                {
                                    if (!regionIndexLookup.ContainsKey(j) && (map.collisionRegions[j].indice1 == i || map.collisionRegions[j].indice2 == i || map.collisionRegions[j].indice3 == i))
                                    {
                                        regionIndexLookup.Add(j, copyRegions.Count);
                                        copyRegions.Add(map.collisionRegions[j]);
                                    }
                                }
                            }
                        }

                        //Get the verts from all the copied regions
                        copyRegionVerts.Clear();
                        regionVertIndexLookup.Clear();
                        for (int i = 0; i < copyRegions.Count; i++)
                        {
                            if (!regionVertIndexLookup.ContainsKey(copyRegions[i].indice1))
                            {
                                regionVertIndexLookup.Add(copyRegions[i].indice1, copyRegionVerts.Count);
                                Vector4 vert = map.collisionVertices[copyRegions[i].indice1];
                                vert.x += copyPosOffset.x;
                                vert.y += copyPosOffset.y;
                                vert.z += copyPosOffset.z;
                                vert.w += copyPosOffset.z;
                                copyRegionVerts.Add(vert);
                            }
                            if (!regionVertIndexLookup.ContainsKey(copyRegions[i].indice2))
                            {
                                regionVertIndexLookup.Add(copyRegions[i].indice2, copyRegionVerts.Count);
                                Vector4 vert = map.collisionVertices[copyRegions[i].indice2];
                                vert.x += copyPosOffset.x;
                                vert.y += copyPosOffset.y;
                                vert.z += copyPosOffset.z;
                                vert.w += copyPosOffset.z;
                                copyRegionVerts.Add(vert);
                            }
                            if (!regionVertIndexLookup.ContainsKey(copyRegions[i].indice3))
                            {
                                regionVertIndexLookup.Add(copyRegions[i].indice3, copyRegionVerts.Count);
                                Vector4 vert = map.collisionVertices[copyRegions[i].indice3];
                                vert.x += copyPosOffset.x;
                                vert.y += copyPosOffset.y;
                                vert.z += copyPosOffset.z;
                                vert.w += copyPosOffset.z;
                                copyRegionVerts.Add(vert);
                            }
                        }

                        //Get RegionSets from copied regions
                        copyRegionSets.Clear();
                        regionSetsIndexLookup.Clear();
                        for (int i = 0; i < copyRegions.Count; i++)
                        {
                            int regionSetIndex = copyRegions[i].regionSetIndex;
                            if (!regionSetsIndexLookup.ContainsKey(regionSetIndex))
                            {
                                regionSetsIndexLookup.Add(regionSetIndex, copyRegionSets.Count);
                                RegionSet rs = map.collisionRegionSets[regionSetIndex];
                                rs.tagID += (short)copyTIDOffset;
                                copyRegionSets.Add(rs);
                            }
                        }

                        //Get Actors
                        copyActors.Clear();
                        for (int i = 0; i < map.actors.Length; i++)
                        {
                            if (copyBounds.Contains(map.actors[i].origin))
                            {
                                ActorInfo actor = ActorInfo.CreateFrom(map.actors[i]);
                                if (actor.TID >= 0)
                                {
                                    actor.TID += copyTIDOffset;
                                }
                                actor.origin += copyPosOffset;
                                for (int j = 0; j < actor.components.Length; j++)
                                {
                                    if (actor.components[j].name == "kexEventLinkComponent")
                                    {
                                        StringStringDictionary dict = actor.components[j].keyValues;
                                        int sourceTID = Convert.ToInt32(dict["sourceTID"]);
                                        int targetTID = Convert.ToInt32(dict["targetTID"]);
                                        sourceTID += copyTIDOffset;
                                        targetTID += copyTIDOffset;
                                        dict["sourceTID"] = Convert.ToString(sourceTID);
                                        dict["targetTID"] = Convert.ToString(targetTID);
                                    }
                                    else if (actor.components[j].name == "kexPathComponent")
                                    {
                                        int n = 1;
                                        while (true)
                                        {
                                            string s;
                                            string key = "path_" + n;
                                            bool hasKey = actor.components[j].keyValues.TryGetValue(key, out s);
                                            if (hasKey)
                                            {
                                                int pathTID = Convert.ToInt32(s);
                                                actor.components[j].keyValues[key] = "" + (pathTID + copyTIDOffset);
                                            }
                                            else
                                            {
                                                break;
                                            }
                                            n++;
                                        }
                                    }
                                    else if (actor.components[j].name == "kexEnemyAIComponent")
                                    {
                                        string s;
                                        bool hasKey = actor.components[j].keyValues.TryGetValue("pathTID", out s);
                                        if (hasKey)
                                        {
                                            int pathTID = Convert.ToInt32(actor.components[j].keyValues["pathTID"]);
                                            if (pathTID >= 0)
                                            {
                                                actor.components[j].keyValues["pathTID"] = "" + (pathTID + copyTIDOffset);
                                            }
                                        }
                                    }
                                }
                                copyActors.Add(actor);
                            }
                        }
                    }

                    if (pastePressed)
                    {
                        int lastRegionVertIndex = map.collisionVertices.Length;
                        int lastRegionSetIndex = map.collisionRegionSets.Length;
                        int lastRegionIndex = map.collisionRegions.Length;

                        //Add staticmeshes to first gridbounds section
                        ArrayUtility.AddRange(ref map.gridBounds.sections[0].staticMeshes, copyStaticMeshes.ToArray());

                        ArrayUtility.AddRange(ref map.actors, copyActors.ToArray());
                        ArrayUtility.AddRange(ref map.collisionVertices, copyRegionVerts.ToArray());
                        ArrayUtility.AddRange(ref map.collisionRegionSets, copyRegionSets.ToArray());

                        ArrayUtility.AddRange(ref map.collisionRegions, copyRegions.ToArray());
                        for (int i = lastRegionIndex; i < map.collisionRegions.Length; i++)
                        {
                            map.collisionRegions[i].regionSetIndex = (short)(lastRegionSetIndex + regionSetsIndexLookup[map.collisionRegions[i].regionSetIndex]);
                            map.collisionRegions[i].indice1 = (short)(lastRegionVertIndex + regionVertIndexLookup[map.collisionRegions[i].indice1]);
                            map.collisionRegions[i].indice2 = (short)(lastRegionVertIndex + regionVertIndexLookup[map.collisionRegions[i].indice2]);
                            map.collisionRegions[i].indice3 = (short)(lastRegionVertIndex + regionVertIndexLookup[map.collisionRegions[i].indice3]);

                            if (regionIndexLookup.ContainsKey(map.collisionRegions[i].edgeLink1))
                                map.collisionRegions[i].edgeLink1 = (short)(lastRegionIndex + regionIndexLookup[map.collisionRegions[i].edgeLink1]);
                            else
                                map.collisionRegions[i].edgeLink1 = -1;
                            if (regionIndexLookup.ContainsKey(map.collisionRegions[i].edgeLink2))
                                map.collisionRegions[i].edgeLink2 = (short)(lastRegionIndex + regionIndexLookup[map.collisionRegions[i].edgeLink2]);
                            else
                                map.collisionRegions[i].edgeLink2 = -1;
                            if (regionIndexLookup.ContainsKey(map.collisionRegions[i].edgeLink3))
                                map.collisionRegions[i].edgeLink3 = (short)(lastRegionIndex + regionIndexLookup[map.collisionRegions[i].edgeLink3]);
                            else
                                map.collisionRegions[i].edgeLink3 = -1;
                        }

                        regionSetsIndexLookup.Clear();
                        regionIndexLookup.Clear();
                        regionVertIndexLookup.Clear();
                        copyActors.Clear();
                        copyStaticMeshes.Clear();
                        copyRegions.Clear();
                        copyRegionSets.Clear();
                        copyRegionVerts.Clear();
                    }
                    break;
                }
                case 4: //Mesh to regions
                {
                    float t2scale = 2.855002f;
                    meshGO = (GameObject)EditorGUILayout.ObjectField("GameObject", meshGO, typeof(GameObject), true);
                    meshRegionCeiling = EditorGUILayout.Toggle("Set Ceiling", meshRegionCeiling);
                    vertsOnly = EditorGUILayout.Toggle("Vertices Only", vertsOnly);
                    meshRegionScale = EditorGUILayout.FloatField("Scale", meshRegionScale);
                    meshRegionYaw = EditorGUILayout.FloatField("Yaw", meshRegionYaw);
                    if (GUILayout.Button("Create Regions"))
                    {
                        Mesh mesh = meshGO.GetComponent<MeshFilter>().sharedMesh;
                        if (mesh != null)
                        {
                            float vertScale = meshRegionScale / t2scale;
                            int lastVertIndex = map.collisionVertices.Length;
                            int lastRegionSetIndex = map.collisionRegionSets.Length;
                            int lastRegionIndex = map.collisionRegions.Length;

                            if (!vertsOnly)
                            {
                                RegionSet regionSet = new RegionSet();
                                regionSet.fogZFar = 1536.0f;
                                regionSet.fogStart = 1024.0f;
                                regionSet.waterZFar = 1536.0f;
                                regionSet.waterStart = 1024.0f;
                                regionSet.waterColor = new Color32(0, 42, 190, 255);
                                regionSet.blendLength = 1.0f;
                                regionSet.cameraMovementScaler = 1.0f;
                                ArrayUtility.Add(ref map.collisionRegionSets, regionSet);

                                int[] meshTris = mesh.triangles;
                                for (int i = 0; i < meshTris.Length; i += 3)
                                {
                                    Region region = new Region();
                                    region.regionSetIndex = (short)lastRegionSetIndex;
                                    //flip the winding order here
                                    region.indice2 = (short)(lastVertIndex + meshTris[i + 0]);
                                    region.indice1 = (short)(lastVertIndex + meshTris[i + 1]);
                                    region.indice3 = (short)(lastVertIndex + meshTris[i + 2]);
                                    region.edgeLink1 = -1;
                                    region.edgeLink2 = -1;
                                    region.edgeLink3 = -1;
                                    ArrayUtility.Add(ref map.collisionRegions, region);
                                }
                            }

                            Vector3[] meshVerts = mesh.vertices;
                            for (int i = 0; i < meshVerts.Length; i++)
                            {
                                Vector3 vert = new Vector3(meshVerts[i].x, meshVerts[i].z, meshVerts[i].y) * vertScale;
                                ArrayUtility.Add(ref map.collisionVertices, vert);
                            }

                            if (!vertsOnly)
                            {
                                //set the region edgelinks
                                for (int i = lastRegionIndex; i < map.collisionRegions.Length; i++)
                                {
                                    int oi1 = map.collisionRegions[i].indice1;
                                    int oi2 = map.collisionRegions[i].indice2;
                                    int oi3 = map.collisionRegions[i].indice3;
                                    for (int j = 0; j < map.collisionRegions.Length; j++)
                                    {
                                        if (i == j)
                                            continue;
                                        int i1 = map.collisionRegions[j].indice1;
                                        int i2 = map.collisionRegions[j].indice2;
                                        int i3 = map.collisionRegions[j].indice3;
                                        //edgelink1 3-1
                                        if (((oi1 == i1 && oi3 == i2) || oi1 == i2 && oi3 == i1) || ((oi1 == i1 && oi3 == i3) || (oi1 == i3 && oi3 == i1)) || ((oi1 == i2 && oi3 == i3) || (oi1 == i3 && oi3 == i2)))
                                            map.collisionRegions[i].edgeLink1 = (short)j;
                                        //edgelink2 1-2
                                        else if (((oi1 == i1 && oi2 == i2) || oi1 == i2 && oi2 == i1) || ((oi1 == i1 && oi2 == i3) || (oi1 == i3 && oi2 == i1)) || ((oi1 == i2 && oi2 == i3) || (oi1 == i3 && oi2 == i2)))
                                            map.collisionRegions[i].edgeLink2 = (short)j;
                                        //edgelink3 2-3
                                        else if (((oi3 == i1 && oi2 == i2) || oi3 == i2 && oi2 == i1) || ((oi3 == i1 && oi2 == i3) || (oi3 == i3 && oi2 == i1)) || ((oi3 == i2 && oi2 == i3) || (oi3 == i3 && oi2 == i2)))
                                            map.collisionRegions[i].edgeLink3 = (short)j;
                                    }
                                }
                            }

                            //set ceiling vert heights with ray casts
                            if (meshRegionCeiling)
                            {
                                for (int i = 0; i < map.collisionVertices.Length; i++)
                                {
                                    RaycastHit rayHit;
                                    Vector3 vert = new Vector3(map.collisionVertices[i].x, map.collisionVertices[i].z, map.collisionVertices[i].y) / vertScale;
                                    if (Physics.Raycast(vert, Vector3.up, out rayHit, Mathf.Infinity))
                                    {
                                        map.collisionVertices[i].w = rayHit.point.y * vertScale;
                                    }
                                }
                            }

                            Vector3 center = new Vector3(0.0f, 0.0f, 0.0f);
                            Quaternion rotation = new Quaternion();
                            rotation.eulerAngles = new Vector3(0.0f, 0.0f, meshRegionYaw);
                            for (int i = 0; i < map.collisionVertices.Length; i++)
                            {
                                Vector3 vert = new Vector3(map.collisionVertices[i].x, map.collisionVertices[i].y, map.collisionVertices[i].z);
                                vert = rotation * (vert - center) + center;
                                map.collisionVertices[i].Set(vert.x, vert.y, vert.z, map.collisionVertices[i].w);
                            }
                        }
                    }
                    break;
                }
                case 5: //Position Offset
                {
                    moveMin = EditorGUILayout.Vector3Field("Area Min", moveMin);
                    moveMax = EditorGUILayout.Vector3Field("Area Max", moveMax);
                    moveOffset = EditorGUILayout.Vector3Field("Offset", moveOffset);
                    bool offsetPressed = GUILayout.Button("Do Offset");
                    if (offsetPressed)
                    {
                        Vector3 boundSize = moveMax - moveMin;
                        Bounds moveBounds = new Bounds(moveMin + boundSize / 2, boundSize);

                        //Staticmeshes
                        for (int i = 0; i < map.gridBounds.sections.Length; i++)
                        {
                            GridSection section = map.gridBounds.sections[i];
                            for (int j = 0; j < section.staticMeshes.Length; j++)
                            {
                                if (moveBounds.Contains(section.staticMeshes[j].origin))
                                {
                                    section.staticMeshes[j].boundsMin += moveOffset;
                                    section.staticMeshes[j].boundsMax += moveOffset;
                                    section.staticMeshes[j].origin += moveOffset;
                                }
                            }
                        }

                        //Vertices
                        for (int i = 0; i < map.collisionVertices.Length; i++)
                        {
                            if (moveBounds.Contains(map.collisionVertices[i]))
                            {
                                map.collisionVertices[i].x += moveOffset.x;
                                map.collisionVertices[i].y += moveOffset.y;
                                map.collisionVertices[i].z += moveOffset.z;
                                map.collisionVertices[i].w += moveOffset.z;
                            }
                        }

                        //Actors
                        for (int i = 0; i < map.actors.Length; i++)
                        {
                            if (moveBounds.Contains(map.actors[i].origin))
                            {
                                map.actors[i].origin += moveOffset;
                            }
                        }
                    }
                    break;
                }
                case 6: //Create Rejects
                {
                    rejectUseFog = EditorGUILayout.Toggle("Region Fog Distance", rejectUseFog);
                    EditorGUI.BeginDisabledGroup(rejectUseFog);
                    rejectRadius = EditorGUILayout.FloatField("Region Radius", rejectRadius);
                    EditorGUI.EndDisabledGroup();
                    bool createPressed = GUILayout.Button("Create Rejects");
                    if (createPressed)
                    {
                        float sqrRadius = rejectRadius * rejectRadius;
                        int nUShorts = Mathf.CeilToInt(map.collisionRegions.Length / 16.0f);
                        map.rejects = new Rejects[map.collisionRegions.Length];
                        for (int i = 0; i < map.collisionRegions.Length; i++)
                        {
                            Vector3 p1 = map.collisionVertices[map.collisionRegions[i].indice1];
                            Vector3 p2 = map.collisionVertices[map.collisionRegions[i].indice2];
                            Vector3 p3 = map.collisionVertices[map.collisionRegions[i].indice3];
                            Vector3 midPoint = (p1 + p2 + p3) / 3;
                            if (rejectUseFog)
                            {
                                float r = map.collisionRegionSets[map.collisionRegions[i].regionSetIndex].fogZFar;
                                sqrRadius = r * r;
                            }

                            map.rejects[i] = new Rejects();
                            map.rejects[i].regions = new ushort[nUShorts];
                            for (int j = 0; j < map.collisionRegions.Length; j++)
                            {
                                Vector3 childP1 = map.collisionVertices[map.collisionRegions[j].indice1];
                                Vector3 childP2 = map.collisionVertices[map.collisionRegions[j].indice2];
                                Vector3 childP3 = map.collisionVertices[map.collisionRegions[j].indice3];
                                Vector3 childMidPoint = (childP1 + childP2 + childP3) / 3;
                                if ((childMidPoint - midPoint).sqrMagnitude < sqrRadius)
                                {
                                    int regionIndex = j / 16;
                                    int bit = j % 16;
                                    map.rejects[i].regions[regionIndex] |= (ushort)(1 << bit);
                                }
                            }
                        }
                    }

                    break;
                }
                case 7: //Find Region
                {
					searchRegionIndex = EditorGUILayout.IntField("Region Index", searchRegionIndex);
                    bool findRegionIndex = GUILayout.Button("Find Region Index");
					if (findRegionIndex)
					{
						for (int i = 0; i < map.collisionRegions.Length; i++)
						{
							if (i == searchRegionIndex)
							{
								Vector4 vert1 = map.collisionVertices[map.collisionRegions[i].indice1];
								Vector4 vert2 = map.collisionVertices[map.collisionRegions[i].indice2];
								Vector4 vert3 = map.collisionVertices[map.collisionRegions[i].indice3];
								Vector3 pos1 = new Vector3(vert1.x, vert1.y, vert1.z);
								Vector3 pos2 = new Vector3(vert2.x, vert2.y, vert2.z);
								Vector3 pos3 = new Vector3(vert3.x, vert3.y, vert3.z);
								Vector3 midPoint = (pos1 + pos2 + pos3) / 3;
								Debug.Log("Found Region " + i + " at pos: " + midPoint + " v1: " + vert1 + " v2: " + vert2 + " v3: " + vert3);
							}
						}
					}

                    searchActorOrigin = EditorGUILayout.Vector3Field("Origin", searchActorOrigin);
                    bool findPressed = GUILayout.Button("Find And Remove");
                    if (findPressed)
                    {
                        for (int i = 0; i < map.collisionRegions.Length; i++)
                        {
                            Vector4 vert1 = map.collisionVertices[map.collisionRegions[i].indice1];
                            Vector4 vert2 = map.collisionVertices[map.collisionRegions[i].indice2];
                            Vector4 vert3 = map.collisionVertices[map.collisionRegions[i].indice3];
                            Vector3 pos1 = new Vector3(vert1.x, vert1.y, vert1.z);
                            Vector3 pos2 = new Vector3(vert2.x, vert2.y, vert2.z);
                            Vector3 pos3 = new Vector3(vert3.x, vert3.y, vert3.z);
                            Vector3 midPoint = (pos1 + pos2 + pos3) / 3;
                            bool posMatch = (Vector3.Distance(midPoint, searchActorOrigin) < 1.0f);
                            if (posMatch)
                            {
                                //Region t = map.collisionRegions[i];
                                //Debug.Log("Region: " + i + " edgeLink1: " + t.edgeLink1 + " edgeLink2: " + t.edgeLink2 + " edgeLink3: " + t.edgeLink3);
                                ArrayUtility.RemoveAt(ref map.collisionRegions, i);
                                Debug.Log("Found And Removed Region " + i);
                                break;
                            }
                        }
                    }

                    bool fixPressed = GUILayout.Button("Checks Regions/Sets");
                    if (fixPressed)
                    {
                        //RegionSets if all are used
                        for (int j = 0; j < map.collisionRegionSets.Length; j++)
                        {
                            bool used = false;
                            for (int i = 0; i < map.collisionRegions.Length; i++)
                            {
                                if (map.collisionRegions[i].regionSetIndex == j)
                                {
                                    used = true;
                                    break;
                                }
                            }
                            if (!used)
                            {
                                Debug.Log("collisionRegionSet " + j + " is unused");
                            }
                        }
                        //check if regions are valid
                        for (int i = 0; i < map.collisionRegions.Length; i++)
                        {
                            //check if indices are valid
                            if (map.collisionRegions[i].indice1 < 0 || map.collisionRegions[i].indice1 >= map.collisionVertices.Length)
                                Debug.Log("Region " + i + " Indice1 is invalid with value of " + map.collisionRegions[i].indice1);
                            if (map.collisionRegions[i].indice2 < 0 || map.collisionRegions[i].indice2 >= map.collisionVertices.Length)
                                Debug.Log("Region " + i + " Indice2 is invalid with value of " + map.collisionRegions[i].indice2);
                            if (map.collisionRegions[i].indice3 < 0 || map.collisionRegions[i].indice3 >= map.collisionVertices.Length)
                                Debug.Log("Region " + i + " Indice3 is invalid with value of " + map.collisionRegions[i].indice3);

                            //check if edge links are valid
                            if (map.collisionRegions[i].edgeLink1 < -1 || map.collisionRegions[i].edgeLink1 > map.collisionRegions.Length)
                                Debug.Log("Region " + i + " edgeLink1 is invalid with value of " + map.collisionRegions[i].edgeLink1);
                            if (map.collisionRegions[i].edgeLink2 < -1 || map.collisionRegions[i].edgeLink2 > map.collisionRegions.Length)
                                Debug.Log("Region " + i + " edgeLink2 is invalid with value of " + map.collisionRegions[i].edgeLink2);
                            if (map.collisionRegions[i].edgeLink3 < -1 || map.collisionRegions[i].edgeLink3 > map.collisionRegions.Length)
                                Debug.Log("Region " + i + " edgeLink3 is invalid with value of " + map.collisionRegions[i].edgeLink3);

                            //check if regionsetindex is valid
                            if (map.collisionRegions[i].regionSetIndex < 0 || map.collisionRegions[i].regionSetIndex >= map.collisionRegionSets.Length)
                            {
                                Debug.Log("Region " + i + " regionSetIndex is invalid with value of " + map.collisionRegions[i].regionSetIndex);
                            }
                        }
                    }
                    break;
                }
            }

            //EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

            //EditorGUILayout.PropertyField(mapProp, new GUIContent("Map"), true);

            serializedObject.ApplyModifiedProperties();
        }
    }
}
