using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using UnityEditor.SceneManagement;

namespace Embatte.IcePlateau
{
    public static class PlateauAssets
    {
        const string Root="Assets/IcePlateau";
        static string Output=>Path.GetFullPath("../artifacts/ice-plateau-v3");
        static Color C(string hex){ColorUtility.TryParseHtmlString("#"+hex,out var color);return color;}
        static Vector3 V(float x,float y,float z)=>new Vector3(x,y,z);
        static float Noise(float x,float y,float z)=>(Mathf.PerlinNoise(x+17.7f,y+8.2f)+Mathf.PerlinNoise(y+5.9f,z+23.3f)+Mathf.PerlinNoise(z+37.1f,x+2.7f))/3;
        static float Fbm(float x,float y,float z){float h=0,weight=.56f;for(int i=0;i<5;i++){h+=Noise(x,y,z)*weight;x=x*2.07f+1.7f;y=y*2.07f+4.1f;z=z*2.07f+7.9f;weight*=.48f;}return h;}
        static float Rim(float a)=>9.1f+.25f*Mathf.Sin(a*3)+.17f*Mathf.Sin(a*7.1f+.8f);
        static void Texture(string path,Color[] pixels,int n,bool normal=false,bool linear=false)
        {
            var texture=new Texture2D(n,n,TextureFormat.RGB24,false);texture.SetPixels(pixels);texture.Apply();File.WriteAllBytes(path,texture.EncodeToPNG());UnityEngine.Object.DestroyImmediate(texture);AssetDatabase.ImportAsset(path);
            var importer=(TextureImporter)AssetImporter.GetAtPath(path);importer.isReadable=true;importer.wrapMode=TextureWrapMode.Repeat;importer.textureCompression=TextureImporterCompression.Uncompressed;importer.maxTextureSize=1024;importer.mipmapEnabled=true;importer.sRGBTexture=!linear&&!normal;if(normal)importer.textureType=TextureImporterType.NormalMap;importer.SaveAndReimport();
        }
        static void Line(float[] pixels,int n,Vector2 a,Vector2 b,float width,float strength)
        {
            var d=b-a;float length=d.sqrMagnitude;int xmin=Mathf.Max(0,(int)Mathf.Min(a.x,b.x)-3),xmax=Mathf.Min(n-1,(int)Mathf.Max(a.x,b.x)+3),ymin=Mathf.Max(0,(int)Mathf.Min(a.y,b.y)-3),ymax=Mathf.Min(n-1,(int)Mathf.Max(a.y,b.y)+3);
            for(int y=ymin;y<=ymax;y++)for(int x=xmin;x<=xmax;x++){var p=new Vector2(x,y);float t=length>0?Mathf.Clamp01(Vector2.Dot(p-a,d)/length):0,v=Mathf.Clamp01((width+.75f-Vector2.Distance(p,a+d*t))/.8f)*strength;pixels[y*n+x]=Mathf.Max(pixels[y*n+x],v);}
        }
        static Material Surface(string name,int type)
        {
            const int n=1024;var colors=new Color[n*n];var normal=new Color[n*n];var packed=new Color[n*n];var heights=new float[n*n];var veins=new float[n*n];var random=new System.Random(3842+type);
            if(type==0)for(int i=0;i<24;i++){
                var a=new Vector2((float)random.NextDouble()*n,(float)random.NextDouble()*n);float angle=(float)random.NextDouble()*Mathf.PI*2;
                for(int k=0;k<7;k++){angle+=((float)random.NextDouble()-.5f)*.7f;var b=a+new Vector2(Mathf.Cos(angle),Mathf.Sin(angle))*(15+random.Next(65));Line(veins,n,a,b,.45f+(i%5==0?.5f:0),.32f);if(k==3)Line(veins,n,a,a+new Vector2(Mathf.Cos(angle+.7f),Mathf.Sin(angle+.7f))*60,.35f,.2f);a=b;}
            }
            for(int y=0;y<n;y++)for(int x=0;x<n;x++){
                float u=(float)x/n,v=(float)y/n,grain=(float)random.NextDouble();int k=y*n+x;float cloud=Fbm(u*7,0,v*7),rough,h;Color color;
                if(type==0){
                    float radius=Mathf.Sqrt((u-.5f)*(u-.5f)+(v-.5f)*(v-.5f))*2;
                    float frost=Mathf.SmoothStep(0,1,(radius-.77f+(cloud-.48f)*.2f)/.2f),mist=Mathf.Clamp01((cloud-.43f)*1.6f);
                    color=Color.Lerp(C("5199b4"),C("b7dce0"),Mathf.Clamp01(cloud*.8f+mist*.6f));color=Color.Lerp(color,C("e7f0ed"),frost*.88f);color=Color.Lerp(color,C("e7f6f5"),veins[k]);
                    float crystals=Noise(u*320,0,v*320);h=cloud*.06f+crystals*.012f+frost*grain*.027f;rough=Mathf.Lerp(.22f+mist*.2f,.88f,frost);
                }else if(type==1){
                    float bands=Mathf.PerlinNoise(u*3+cloud*1.2f,v*55),flutes=Mathf.PerlinNoise(u*95,v*2.4f),clefts=Mathf.Pow(Mathf.Clamp01((.48f-flutes)*3.5f),2),depth=Mathf.SmoothStep(0,1,v);
                    color=Color.Lerp(C("154860"),C("81bed2"),depth*.76f+cloud*.22f);color*=.80f+flutes*.29f;float stratum=Mathf.Pow(Mathf.Max(0,Mathf.Sin((v*16+cloud*.6f)*Mathf.PI*2)),16);color=Color.Lerp(color,C("bce5e7"),stratum*.18f);color=Color.Lerp(color,C("153f57"),clefts*.23f);
                    h=flutes*.11f+bands*.045f-clefts*.1f;rough=.32f+cloud*.22f;
                }else{
                    float lat=v*Mathf.PI,lon=u*Mathf.PI*2;var p=V(Mathf.Sin(lat)*Mathf.Cos(lon),Mathf.Cos(lat),Mathf.Sin(lat)*Mathf.Sin(lon));float broad=Fbm(p.x*7,p.y*7,p.z*7),granules=Noise(p.x*135,p.y*135,p.z*135),powder=Noise(p.x*410,p.y*410,p.z*410);
                    color=Color.Lerp(C("c8d5da"),C("fafbf5"),.7f+broad*.27f);color*=.94f+granules*.08f;h=broad*.09f+granules*.1f+powder*.042f;rough=.85f+powder*.13f;
                }
                color.a=1;colors[k]=color;heights[k]=h;packed[k]=new Color(1,rough,1,1);
            }
            for(int y=0;y<n;y++)for(int x=0;x<n;x++){float dx=heights[y*n+(x+1)%n]-heights[y*n+(x+n-1)%n],dy=heights[((y+1)%n)*n+x]-heights[((y+n-1)%n)*n+x];var value=V(-dx*(type==2?12:8),-dy*(type==2?12:8),1).normalized;normal[y*n+x]=new Color(value.x*.5f+.5f,value.y*.5f+.5f,value.z*.5f+.5f);}
            string prefix=Root+"/Textures/"+name;Texture(prefix+"-Color.png",colors,n);Texture(prefix+"-Normal.png",normal,n,true);Texture(prefix+"-Roughness.png",packed,n,false,true);
            var material=new Material(Shader.Find("Standard")){name=name,color=Color.white};material.mainTexture=AssetDatabase.LoadAssetAtPath<Texture2D>(prefix+"-Color.png");material.SetTexture("_BumpMap",AssetDatabase.LoadAssetAtPath<Texture2D>(prefix+"-Normal.png"));material.SetFloat("_BumpScale",type==2?.75f:.7f);material.SetFloat("_Glossiness",type==2?.08f:.52f);material.SetFloat("_Metallic",0);material.EnableKeyword("_NORMALMAP");material.SetOverrideTag("EmbatteRoughness",Path.GetFullPath(prefix+"-Roughness.png"));AssetDatabase.CreateAsset(material,Root+"/Materials/"+name+".mat");return material;
        }
        static Mesh Mesh(string name,List<Vector3> vertices,List<Vector2> uv,List<int> indices)
        {
            var mesh=new Mesh{name=name,indexFormat=IndexFormat.UInt32};mesh.SetVertices(vertices);mesh.SetUVs(0,uv);mesh.SetTriangles(indices,0);mesh.RecalculateNormals();var normals=mesh.normals;for(int i=0;i<normals.Length;i++)if(normals[i].sqrMagnitude<.1f)normals[i]=Vector3.up;mesh.normals=normals;mesh.RecalculateTangents();mesh.RecalculateBounds();return mesh;
        }
        static void Part(GameObject root,string name,Mesh mesh,Material mat)
        {
            var part=new GameObject(name);part.transform.SetParent(root.transform,false);part.AddComponent<MeshFilter>().sharedMesh=mesh;part.AddComponent<MeshRenderer>().sharedMaterial=mat;
        }
        static GameObject Plateau(Material top,Material wall)
        {
            var root=new GameObject("IcePlateau");var vertices=new List<Vector3>();var uv=new List<Vector2>();var indices=new List<int>();const int sides=192,rings=16,levels=24;
            for(int j=0;j<=rings;j++)for(int i=0;i<=sides;i++){float a=i*Mathf.PI*2/sides,r=Rim(a)*j/rings,x=Mathf.Sin(a)*r,z=Mathf.Cos(a)*r;vertices.Add(V(x,0,z));uv.Add(new Vector2(x/19f+.5f,z/19f+.5f));if(j<rings&&i<sides){int p=j*(sides+1)+i,q=p+sides+1;indices.AddRange(new[]{p,q,p+1,p+1,q,q+1});}}
            Part(root,"SolidFrozenLake",Mesh("Unbroken polished ice",vertices,uv,indices),top);vertices.Clear();uv.Clear();indices.Clear();
            for(int j=0;j<=levels;j++)for(int i=0;i<=sides;i++){
                float a=i*Mathf.PI*2/sides,t=(float)j/levels;float groove=(Noise(Mathf.Sin(a)*22,t*1.8f,Mathf.Cos(a)*22)-.5f)*.3f*Mathf.Sin(t*Mathf.PI),taper=Mathf.SmoothStep(0,1,t)*.8f;
                float radius=Rim(a)-taper+groove-.08f*Mathf.Sin(a*37)*Mathf.Sin(t*Mathf.PI);float depth=9.3f+(Noise(Mathf.Sin(a)*7,0,Mathf.Cos(a)*7)-.5f)*1.7f;
                vertices.Add(V(Mathf.Sin(a)*radius,-t*depth,Mathf.Cos(a)*radius));uv.Add(new Vector2(i/48f,1-t));if(j<levels&&i<sides){int p=j*(sides+1)+i,q=p+sides+1;indices.AddRange(new[]{p,q,p+1,p+1,q,q+1});}
            }
            Part(root,"TallBlueIceCliff",Mesh("Nine metre glacial drop",vertices,uv,indices),wall);return root;
        }
        static GameObject Snowball(Material material)
        {
            var root=new GameObject("NaturalSnowball");var vertices=new List<Vector3>();var uv=new List<Vector2>();var indices=new List<int>();const int sides=64,rows=40;
            for(int j=0;j<=rows;j++)for(int i=0;i<=sides;i++){float u=(float)i/sides,v=(float)j/rows;var p=V(Mathf.Sin(v*Mathf.PI)*Mathf.Cos(u*Mathf.PI*2),Mathf.Cos(v*Mathf.PI),Mathf.Sin(v*Mathf.PI)*Mathf.Sin(u*Mathf.PI*2));float radius=1+(Fbm(p.x*5,p.y*5,p.z*5)-.5f)*.038f+(Noise(p.x*34,p.y*34,p.z*34)-.5f)*.009f;vertices.Add(p*radius);uv.Add(new Vector2(u,1-v));if(j<rows&&i<sides){int a=j*(sides+1)+i,b=a+sides+1;indices.AddRange(new[]{a,a+1,b,a+1,b+1,b});}}
            var mesh=Mesh("Soft compacted natural snow",vertices,uv,indices);var normals=mesh.normals;for(int j=0;j<=rows;j++){int a=j*(sides+1),b=a+sides;var value=(normals[a]+normals[b]).normalized;normals[a]=normals[b]=value;}for(int i=0;i<=sides;i++){normals[i]=Vector3.up;normals[rows*(sides+1)+i]=Vector3.down;}mesh.normals=normals;mesh.RecalculateTangents();Part(root,"Plain snow sphere",mesh,material);return root;
        }
        static void Save(GameObject root)
        {
            foreach(var f in root.GetComponentsInChildren<MeshFilter>())AssetDatabase.CreateAsset(f.sharedMesh,Root+"/Meshes/"+f.sharedMesh.name+".asset");PrefabUtility.SaveAsPrefabAsset(root,Root+"/Prefabs/"+root.name+".prefab");Tinta.Editor.NativeExport.Write(root,Output+"/"+root.name+".json");
        }
        static void Capture(Camera camera,string path,int w,int h)
        {
            var target=new RenderTexture(w,h,24){antiAliasing=4};target.Create();camera.targetTexture=target;camera.Render();var previous=RenderTexture.active;RenderTexture.active=target;var texture=new Texture2D(w,h,TextureFormat.RGB24,false);texture.ReadPixels(new Rect(0,0,w,h),0,0);texture.Apply();File.WriteAllBytes(path,texture.EncodeToPNG());camera.targetTexture=null;RenderTexture.active=previous;target.Release();UnityEngine.Object.DestroyImmediate(target);UnityEngine.Object.DestroyImmediate(texture);
        }
        [MenuItem("Embatte/Gerar lago suspenso")]
        public static void Generate()
        {
            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene,NewSceneMode.Single);foreach(string folder in new[]{"Textures","Materials","Meshes","Prefabs","Scenes"}){string path=Root+"/"+folder;if(AssetDatabase.IsValidFolder(path))AssetDatabase.DeleteAsset(path);Directory.CreateDirectory(path);}Directory.CreateDirectory(Output);AssetDatabase.Refresh();PlayerSettings.colorSpace=ColorSpace.Linear;GraphicsSettings.defaultRenderPipeline=null;QualitySettings.renderPipeline=null;
            var top=Surface("FrozenLake",0);var wall=Surface("LayeredBlueIce",1);var snow=Surface("FineSnow",2);var plateau=Plateau(top,wall);var ball=Snowball(snow);Save(plateau);Save(ball);
            RenderSettings.ambientMode=AmbientMode.Trilight;RenderSettings.ambientSkyColor=C("c3dce7")*.7f;RenderSettings.ambientEquatorColor=C("638fa3")*.5f;RenderSettings.ambientGroundColor=C("243a50")*.3f;
            var light=new GameObject("Clear northern daylight").AddComponent<Light>();light.type=LightType.Directional;light.color=C("fff0d4");light.intensity=1.2f;light.shadows=LightShadows.Soft;light.shadowBias=.03f;light.shadowNormalBias=.1f;QualitySettings.shadowDistance=80;light.transform.rotation=Quaternion.Euler(38,-38,0);var fill=new GameObject("Soft blue bounce").AddComponent<Light>();fill.type=LightType.Directional;fill.color=C("abcde5");fill.intensity=.3f;fill.transform.rotation=Quaternion.Euler(35,135,0);
            var camera=new GameObject("Plateau review camera").AddComponent<Camera>();camera.clearFlags=CameraClearFlags.SolidColor;camera.backgroundColor=C("172d42");camera.fieldOfView=36;camera.farClipPlane=150;ball.SetActive(false);camera.transform.position=V(18,24,32);camera.transform.LookAt(V(0,-2,0));Capture(camera,Output+"/IcePlateau.png",1200,1200);
            plateau.SetActive(false);ball.SetActive(true);camera.transform.position=V(2.3f,1.2f,4.1f);camera.transform.LookAt(Vector3.zero);Capture(camera,Output+"/NaturalSnowball.png",900,900);
            plateau.SetActive(true);ball.SetActive(false);for(int i=0;i<4;i++){var copy=UnityEngine.Object.Instantiate(ball);copy.SetActive(true);float r=.6f+i*.25f;copy.transform.localScale=Vector3.one*r;copy.transform.position=V(i%2==0?-3.1f:3.1f,r,i<2?3.5f:-2.5f);}
            camera.transform.position=V(0,23,30);camera.transform.LookAt(V(0,-1.5f,0));Capture(camera,Output+"/plateau-game-view.png",1400,1100);EditorSceneManager.SaveScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene(),Root+"/Scenes/LagoSuspenso.unity");AssetDatabase.SaveAssets();AssetDatabase.ExportPackage(new[]{Root,"Assets/Tinta","Assets/Vassouras","Assets/EmbatteStudio","Assets/Glacier"},Output+"/Embatte-LagoSuspenso-v3.unitypackage",ExportPackageOptions.Recurse);Debug.Log("EMBATTE_ICE_PLATEAU_OK");
        }
    }
}
