using System;
using System.Collections.Generic;
using AwesomeTechnologies.VegetationStudio;
using UnityEngine;
using AwesomeTechnologies.VegetationSystem;
using AwesomeTechnologies.VegetationSystem.Biomes;
using Unity.Jobs;
#if UNITY_EDITOR
using UnityEditor;
#endif

#if UNITY_2018_3_OR_NEWER
      using System.IO;
      #endif


namespace AwesomeTechnologies.TerrainSystem
{
    public partial class TerrainSystemPro : MonoBehaviour
    {
        public VegetationSystemPro VegetationSystemPro;
        public int CurrentTabIndex;
        public int VegetationPackageIndex;
        public int VegetationPackageTextureIndex;

        public bool ShowCurvesMenu = true;
        public bool ShowNoiseMenu = true;

        // ReSharper disable once UnusedMember.Local
        void Reset()
        {
            VegetationSystemPro = GetComponent<VegetationSystemPro>();
        }

        List<IVegetationStudioTerrain> GetOverlapTerrainList(Bounds updateBounds)
        {
            List<IVegetationStudioTerrain> overlapTerrainList = new List<IVegetationStudioTerrain>();
            for (int i = 0; i <= VegetationSystemPro.VegetationStudioTerrainList.Count - 1; i++)
            {
                if (VegetationSystemPro.VegetationStudioTerrainList[i]
                    .NeedsSplatMapUpdate(updateBounds))
                {
                    overlapTerrainList.Add(VegetationSystemPro.VegetationStudioTerrainList[i]);
                }
            }

            return overlapTerrainList;
        }

        void PrepareTextureCurves()
        {
            for (int i = 0; i <= VegetationSystemPro.VegetationPackageProList.Count - 1; i++)
            {
                VegetationSystemPro.VegetationPackageProList[i].PrepareNativeArrayTextureCurves();
            }
        }

        void DisposeTextureCurves()
        {
            for (int i = 0; i <= VegetationSystemPro.VegetationPackageProList.Count - 1; i++)
            {
                VegetationSystemPro.VegetationPackageProList[i].DisposeNativeArrayTextureCurves();
            }
        }

        public Texture2D GetTerrainTexture(int index)
        {
            for (int i = 0; i <= VegetationSystemPro.VegetationStudioTerrainList.Count - 1; i++)
            {
                IVegetationStudioTerrain iVegetationStudioTerrain = VegetationSystemPro.VegetationStudioTerrainList[i];
                if (iVegetationStudioTerrain.HasTerrainTextures())
                {
                    return iVegetationStudioTerrain.GetTerrainTexture(index);
                }
            }
            return null;
        }

        public void GetSplatPrototypesFromTerrain(VegetationPackagePro vegetationPackage)
        {
            for (int i = 0; i <= VegetationSystemPro.VegetationStudioTerrainList.Count - 1; i++)
            {
                IVegetationStudioTerrain iVegetationStudioTerrain = VegetationSystemPro.VegetationStudioTerrainList[i];
                if (iVegetationStudioTerrain.HasTerrainTextures())
                {
                    
#if UNITY_2018_3_OR_NEWER
                    TerrainLayer[] terrainLayers = iVegetationStudioTerrain.GetTerrainLayers();

                    for (int j = 0; j <= vegetationPackage.TerrainTextureList.Count - 1; j++)
                    {
                        if (j < terrainLayers.Length)
                        {
                            vegetationPackage.TerrainTextureList[j].Texture = terrainLayers[j].diffuseTexture;
                            vegetationPackage.TerrainTextureList[j].TextureNormals = terrainLayers[j].normalMapTexture;
                            vegetationPackage.TerrainTextureList[j].Offset = terrainLayers[j].tileOffset;
                            vegetationPackage.TerrainTextureList[j].TileSize = terrainLayers[j].tileSize;
                        }
                    }

                    break;
#else
  SplatPrototype[] splatPrototypes = iVegetationStudioTerrain.GetSplatPrototypes();

                    for (int j = 0; j <= vegetationPackage.TerrainTextureList.Count - 1; j++)
                    {
                        if (j < splatPrototypes.Length)
                        {
                            vegetationPackage.TerrainTextureList[j].Texture = splatPrototypes[j].texture;
                            vegetationPackage.TerrainTextureList[j].TextureNormals = splatPrototypes[j].normalMap;
                            vegetationPackage.TerrainTextureList[j].Offset = splatPrototypes[j].tileOffset;
                            vegetationPackage.TerrainTextureList[j].TileSize = splatPrototypes[j].tileSize;
                        }
                    }
                    break;
#endif                                     
                }
            }
        }

        public void SetSplatPrototypes(VegetationPackagePro vegetationPackage)
        {
#if UNITY_2018_3_OR_NEWER
                       TerrainLayer[] terrainLayers = new TerrainLayer[vegetationPackage.TerrainTextureList.Count];
            for (int i = 0; i <= vegetationPackage.TerrainTextureList.Count - 1; i++)
            {
                TerrainTextureInfo terrainTextureInfo = vegetationPackage.TerrainTextureList[i];
                TerrainLayer terrainLayer = terrainTextureInfo.TerrainLayer;

                if (terrainLayer == null)
                {
                    terrainLayer = new TerrainLayer
                    {
                        diffuseTexture = terrainTextureInfo.Texture,
                        normalMapTexture = terrainTextureInfo.TextureNormals,
                        tileSize = terrainTextureInfo.TileSize,
                        tileOffset = terrainTextureInfo.Offset                                                  
                    };     
#if UNITY_EDITOR
                    if (!Application.isPlaying)
                    {
                        terrainLayer = SaveTerrainLayer(terrainLayer,vegetationPackage);                  
                    }
                    EditorUtility.SetDirty(vegetationPackage);
#endif
                    terrainTextureInfo.TerrainLayer = terrainLayer;
                }
                else
                {
                    terrainLayer.diffuseTexture = terrainTextureInfo.Texture;
                    terrainLayer.normalMapTexture = terrainTextureInfo.TextureNormals;
                    terrainLayer.tileSize = terrainTextureInfo.TileSize;
                    terrainLayer.tileOffset = terrainTextureInfo.Offset;
#if UNITY_EDITOR
                    EditorUtility.SetDirty(terrainLayer);
#endif
                }                                                       
                terrainLayers[i] = terrainLayer;                
            }

            for (int i = 0; i <= VegetationSystemPro.VegetationStudioTerrainList.Count - 1; i++)
            {
                IVegetationStudioTerrain iVegetationStudioTerrain = VegetationSystemPro.VegetationStudioTerrainList[i];
                if (iVegetationStudioTerrain.HasTerrainTextures())
                {
                    iVegetationStudioTerrain.SetTerrainLayers(terrainLayers);
                }
            }              
#else                        
            SplatPrototype[] splatPrototypes = new SplatPrototype[vegetationPackage.TerrainTextureList.Count];
            for (int i = 0; i <= vegetationPackage.TerrainTextureList.Count - 1; i++)
            {
                TerrainTextureInfo terrainTextureInfo = vegetationPackage.TerrainTextureList[i];

                SplatPrototype splatPrototype = new SplatPrototype
                {
                    texture = terrainTextureInfo.Texture,
                    normalMap = terrainTextureInfo.TextureNormals,
                    tileSize = terrainTextureInfo.TileSize,
                    tileOffset = terrainTextureInfo.Offset                   
                };
                splatPrototypes[i] = splatPrototype;
            }

            for (int i = 0; i <= VegetationSystemPro.VegetationStudioTerrainList.Count - 1; i++)
            {
                IVegetationStudioTerrain iVegetationStudioTerrain = VegetationSystemPro.VegetationStudioTerrainList[i];
                if (iVegetationStudioTerrain.HasTerrainTextures())
                {
                    iVegetationStudioTerrain.SetSplatPrototypes(splatPrototypes);
                }
            }  
#endif
        }

        
#if UNITY_2018_3_OR_NEWER
        private TerrainLayer SaveTerrainLayer(TerrainLayer terrainLayer, VegetationPackagePro vegetationPackagePro)
        {
#if UNITY_EDITOR
            if (!vegetationPackagePro) return terrainLayer;

            string terrainDataPath = AssetDatabase.GetAssetPath(vegetationPackagePro);
            var directory = Path.GetDirectoryName(terrainDataPath);
            
            var filename = Path.GetFileNameWithoutExtension(terrainDataPath);
            var folderName = filename + "_TerrainLayers";

            if (!AssetDatabase.IsValidFolder(directory + "/" + folderName))
                AssetDatabase.CreateFolder(directory, folderName);            
            
            terrainDataPath = terrainDataPath.Replace(".asset", "");
            string newTerrainLayerDataPath = directory + "/" + folderName + "/_TerrainLayer_" + Guid.NewGuid().ToString() + ".asset";
            AssetDatabase.CreateAsset(terrainLayer, newTerrainLayerDataPath);
            AssetDatabase.SaveAssets();
            return AssetDatabase.LoadAssetAtPath<TerrainLayer>(newTerrainLayerDataPath);
#else
            return null;
#endif
        }
#endif

        public void GenerateSplatMap(bool clearLockedTextures, IVegetationStudioTerrain iVegetationStudioTerrain)
        {
            if (!VegetationSystemPro) return;

            VegetationSystemPro.ClearCache(iVegetationStudioTerrain.TerrainBounds);            
            PrepareTextureCurves();
            
            float worldspaceMinHeight = VegetationSystemPro.VegetationSystemBounds.center.y -
                                        VegetationSystemPro.VegetationSystemBounds.extents.y;
            float worldspaceSeaLevel = worldspaceMinHeight + VegetationSystemPro.SeaLevel;
            float worldspaceMaxHeight = VegetationSystemPro.VegetationSystemBounds.center.y +
                                        VegetationSystemPro.VegetationSystemBounds.extents.y;
            float heightCurveSampleHeight = worldspaceMaxHeight - worldspaceSeaLevel;
                                 
            VegetationPackagePro defaultBiomeVegetationPackagePro =
                VegetationSystemPro.GetVegetationPackageFromBiome(BiomeType.Default);

            if (defaultBiomeVegetationPackagePro == null)
            {
                Debug.LogWarning("You need a default biome in order to generate splatmaps. ");
                return;
            }

            iVegetationStudioTerrain.PrepareSplatmapGeneration(clearLockedTextures);                
            
            iVegetationStudioTerrain.GenerateSplatMapBiome(VegetationSystemPro.VegetationSystemBounds,
                    BiomeType.Default, null, defaultBiomeVegetationPackagePro.TerrainTextureSettingsList,
                    heightCurveSampleHeight, worldspaceSeaLevel, clearLockedTextures);
            
            List<BiomeType> additionalBiomeList = VegetationSystemPro.GetAdditionalBiomeList();            

            List<VegetationPackagePro> additionalVegetationPackageList = new List<VegetationPackagePro>();
            for (int i = 0; i <= additionalBiomeList.Count - 1; i++)
            {
                additionalVegetationPackageList.Add(
                    VegetationSystemPro.GetVegetationPackageFromBiome(additionalBiomeList[i]));
            }
            BiomeSortOrderComparer biomeSortOrderComparer = new BiomeSortOrderComparer();
            additionalVegetationPackageList.Sort(biomeSortOrderComparer);

            for (int i = 0; i <= additionalVegetationPackageList.Count - 1; i++)
            {
                VegetationPackagePro currentVegetationPackagePro = additionalVegetationPackageList[i];
                if (!currentVegetationPackagePro.GenerateBiomeSplamap) continue;                
                
                List<PolygonBiomeMask> biomeMaskList = VegetationStudioManager.GetBiomeMasks(currentVegetationPackagePro.BiomeType);
                iVegetationStudioTerrain.GenerateSplatMapBiome(VegetationSystemPro.VegetationSystemBounds,
                        currentVegetationPackagePro.BiomeType, biomeMaskList, currentVegetationPackagePro.TerrainTextureSettingsList, heightCurveSampleHeight, worldspaceSeaLevel,clearLockedTextures);
                                               
            }

            JobHandle.ScheduleBatchedJobs();
            iVegetationStudioTerrain.CompleteSplatmapGeneration();
            DisposeTextureCurves();
        }

        public void GenerateSplatMap(bool clearLockedTextures)
        {
            List<IVegetationStudioTerrain> overlapTerrainList = GetOverlapTerrainList(VegetationSystemPro.VegetationSystemBounds);
            int terrainCount = overlapTerrainList.Count;
            for (int i = 0; i <= overlapTerrainList.Count - 1; i++)
            {
#if UNITY_EDITOR
                EditorUtility.DisplayProgressBar("Generate splatmap",
                    "Terrain " + (i + 1) + "/" + terrainCount, (i + 1) / (float) terrainCount);
#endif
                GenerateSplatMap(clearLockedTextures, overlapTerrainList[i]);
                GC.Collect();
            }
#if UNITY_EDITOR
            EditorUtility.ClearProgressBar();
#endif
            
        }

       public void GenerateSplatMapParallel(bool clearLockedTextures)
        {
            if (!VegetationSystemPro) return;
            
            VegetationSystemPro.ClearCache();
            
            List<IVegetationStudioTerrain> overlapTerrainList =
                GetOverlapTerrainList(VegetationSystemPro.VegetationSystemBounds);
            PrepareTextureCurves();

            float worldspaceMinHeight = VegetationSystemPro.VegetationSystemBounds.center.y -
                                        VegetationSystemPro.VegetationSystemBounds.extents.y;
            float worldspaceSeaLevel = worldspaceMinHeight + VegetationSystemPro.SeaLevel;
            float worldspaceMaxHeight = VegetationSystemPro.VegetationSystemBounds.center.y +
                                        VegetationSystemPro.VegetationSystemBounds.extents.y;
            float heightCurveSampleHeight = worldspaceMaxHeight - worldspaceSeaLevel;


            VegetationPackagePro defaultBiomeVegetationPackagePro =
                VegetationSystemPro.GetVegetationPackageFromBiome(BiomeType.Default);

            if (defaultBiomeVegetationPackagePro == null)
            {
                Debug.LogWarning("You need a default biome in order to generate splatmaps. ");
                return;
            }

            int terrainCount = overlapTerrainList.Count;
            for (int i = 0; i <= overlapTerrainList.Count - 1; i++)
            {
                                
                overlapTerrainList[i].PrepareSplatmapGeneration(clearLockedTextures);                
#if UNITY_EDITOR
                EditorUtility.DisplayProgressBar("Prepare generation",
                    "Terrain " + (i + 1) + "/" + terrainCount, (i + 1) / (float) terrainCount);
#endif                
            }
            
            for (int i = 0; i <= overlapTerrainList.Count - 1; i++)
            {
                overlapTerrainList[i].GenerateSplatMapBiome(VegetationSystemPro.VegetationSystemBounds,
                    BiomeType.Default, null, defaultBiomeVegetationPackagePro.TerrainTextureSettingsList, heightCurveSampleHeight, worldspaceSeaLevel,clearLockedTextures);
                
#if UNITY_EDITOR
                EditorUtility.DisplayProgressBar("Generating default biome",
                    "Terrain " + (i + 1) + "/" + terrainCount, (i + 1) / (float) terrainCount);
#endif
            }

            List<BiomeType> additionalBiomeList = VegetationSystemPro.GetAdditionalBiomeList();
            

            List<VegetationPackagePro> additionalVegetationPackageList = new List<VegetationPackagePro>();
            for (int i = 0; i <= additionalBiomeList.Count - 1; i++)
            {
                additionalVegetationPackageList.Add(
                    VegetationSystemPro.GetVegetationPackageFromBiome(additionalBiomeList[i]));
            }
            BiomeSortOrderComparer biomeSortOrderComparer = new BiomeSortOrderComparer();
            additionalVegetationPackageList.Sort(biomeSortOrderComparer);

            for (int i = 0; i <= additionalVegetationPackageList.Count - 1; i++)
            {
                VegetationPackagePro currentVegetationPackagePro = additionalVegetationPackageList[i];
                if (!currentVegetationPackagePro.GenerateBiomeSplamap) continue;                
                
                List<PolygonBiomeMask> biomeMaskList = VegetationStudioManager.GetBiomeMasks(currentVegetationPackagePro.BiomeType);
                for (int j = 0; j <= overlapTerrainList.Count - 1; j++)
                {
                    overlapTerrainList[j].GenerateSplatMapBiome(VegetationSystemPro.VegetationSystemBounds,
                        currentVegetationPackagePro.BiomeType, biomeMaskList, currentVegetationPackagePro.TerrainTextureSettingsList, heightCurveSampleHeight, worldspaceSeaLevel,clearLockedTextures);
                }                               
            }

            JobHandle.ScheduleBatchedJobs();

           
            for (int i = 0; i <= overlapTerrainList.Count - 1; i++)
            {
#if UNITY_EDITOR
                      EditorUtility.DisplayProgressBar("Updating terrain",
                          "Terrain " + (i + 1) + "/" + terrainCount, (i + 1) / (float) terrainCount);
      #endif
                overlapTerrainList[i].CompleteSplatmapGeneration();
            }
#if UNITY_EDITOR
            EditorUtility.ClearProgressBar();
#endif
            DisposeTextureCurves();
        }

      
    }
}