﻿#if UNITY_EDITOR
using UnityEditor;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using Sirenix.Utilities.Editor;
using Sirenix.Utilities;
#endif
using UnityEngine;
using System.Collections.Generic;
using System.IO;

public class GradientGenerator : OdinEditorWindow
{
    [MenuItem("Tools/GradientGenerator")]
    private static void OpenWindow()
    {
        var window = GetWindow<GradientGenerator>();

        // Nifty little trick to quickly position the window in the middle of the editor.
        window.position = GUIHelper.GetEditorWindowRect().AlignCenter(380, 500);
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        UpdateTextFilesAvailable();
    }

    [TabGroup("Gradient Group", "Saving and Loading")]
    public string textureName;
    
    [TabGroup("Gradient Group", "Generator")]
    [HorizontalGroup("Gradient Group/Generator/Splity", 130), PropertyOrder(-1)]
    [VerticalGroup("Gradient Group/Generator/Splity/Left")]
    [HideLabel, PreviewField(128)]
    public Texture2D workingTexture;

    [VerticalGroup("Gradient Group/Generator/Splity/Left")]
    [Button(ButtonSizes.Large), GUIColor(0.4f, 0.8f, 1)]
    public void Generate()
    {
        GenerateTexture();
    }

    [TabGroup("Gradient Group", "Generator")]
    [HorizontalGroup("Gradient Group/Generator/Splity")]
    [ListDrawerSettings(DraggableItems = true, Expanded = true, ShowIndexLabels = false, ShowPaging = false, ShowItemCount = false)]
    public List<Gradient> gradients = new List<Gradient>();
    
    [TabGroup("Gradient Group", "Materials")]
    public Transform transformToCheck;

    [TabGroup("Gradient Group", "Materials")]
    public List<Material> updateMaterials = new List<Material>();
    
    [TabGroup("Gradient Group", "Materials")]
    public UnityEngine.Texture2D TextureToSet;

    [TabGroup("Gradient Group", "Materials")]
    [DisableIf("EmptyTexture")]
    [Button(ButtonSizes.Large), GUIColor(0.6f, 0.5f, 1)]
    public void SetTextureOnMaterials()
    {
        for (int i = 0; i < updateMaterials.Count; i++)
        {
            updateMaterials[i].mainTexture = TextureToSet;
        }
    }


    Dictionary<Material, Texture2D> oldTextureDictionary = new Dictionary<Material, Texture2D>();

    public static Texture2D DoGenerateTexture(ref List<Gradient> gradients)
    {
        Texture2D newTexture2D = new Texture2D(256, 256);
        return DoGenerateTexture(ref gradients, ref newTexture2D);
    }
    public static Texture2D DoGenerateTexture(ref List<Gradient> gradients, ref Texture2D TextureToUse)
    {
        float amountDivisor = TextureToUse.width / gradients.Count;

        for (int gradient = 0; gradient < gradients.Count; gradient++)
        {
            int maxValue = Mathf.Clamp(Mathf.CeilToInt(amountDivisor * (gradient + 1)), 0, TextureToUse.width);
            int minValue = Mathf.Clamp(maxValue - Mathf.CeilToInt(amountDivisor), 0, TextureToUse.width);
            //Debug.Log("Current gradient " + gradient + " minvalue " + minValue + " maxvalue " + maxValue);

            if (minValue >= maxValue)
            {
                Debug.Log("ERROR");
            }
            {
                for (int x = minValue; x <= maxValue; x++)
                {
                    for (int y = TextureToUse.height - 1; y >= 0; y--)
                    {

                        float evaluate = Remap(y, 0, TextureToUse.height, 1, 0);
                        TextureToUse.SetPixel(x, y, gradients[gradient].Evaluate(evaluate));
                        //Debug.Log("x " + x + " y " + y + " color " + gradients[gradient].Evaluate(evaluate));
                    }
                }
            }
        }

        TextureToUse.Apply(true);

        return TextureToUse;
    }

    public void GenerateTexture()
    {
        if (workingTexture == null)
        {
            workingTexture = new Texture2D(256, 256);
            workingTexture.name = "Working";
        }

        DoGenerateTexture(ref gradients, ref workingTexture);
        
        ApplyTextureToMaterials();
    }

    private void ApplyTextureToMaterials()
    {
        if (transformToCheck != null)
        {
            var skinnedMesh = transformToCheck.gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
            if (skinnedMesh != null)
            {
                if (skinnedMesh.sharedMaterial.mainTexture != null &&
                    !string.IsNullOrEmpty(skinnedMesh.sharedMaterial.mainTexture.name) &&
                    skinnedMesh.sharedMaterial.mainTexture != workingTexture)
                {
                    oldTextureDictionary[skinnedMesh.sharedMaterial] = (Texture2D) skinnedMesh.sharedMaterial.mainTexture;
                }
                skinnedMesh.sharedMaterial.mainTexture = workingTexture;
            }
            else
            {
                var mesh = transformToCheck.gameObject.GetComponentInChildren<MeshRenderer>();
                if (mesh != null)
                {
                    if (mesh.sharedMaterial.mainTexture != null &&
                        !string.IsNullOrEmpty(mesh.sharedMaterial.mainTexture.name) &&
                        mesh.sharedMaterial.mainTexture != workingTexture)
                    {
                        oldTextureDictionary[mesh.sharedMaterial] = (Texture2D) mesh.sharedMaterial.mainTexture;
                    }
                    mesh.sharedMaterial.mainTexture = workingTexture;
                }
            }
        }
        if (updateMaterials.Count > 0)
        {
            foreach (var materialToUpdate in updateMaterials)
            {
                if (materialToUpdate.mainTexture != null &&
                    !string.IsNullOrEmpty(materialToUpdate.mainTexture.name) &&
                    materialToUpdate.mainTexture != workingTexture)
                {
                    oldTextureDictionary[materialToUpdate] = (Texture2D) materialToUpdate.mainTexture;
                }
                materialToUpdate.mainTexture = workingTexture;
            }
        }
    }


    [TabGroup("Gradient Group", "Saving and Loading")]
    [HorizontalGroup("Gradient Group/Saving and Loading/Split2", 0.5f)]
    [Button(ButtonSizes.Large), GUIColor(0.4f, 0.8f, 1)]
    [DisableIf("EmptyName")]
    public void saveTexture()
    {
        if(workingTexture == null) GenerateTexture();

        SaveTexture(workingTexture, textureName, false);
    }

    //[HorizontalGroup("Split2", 0.5f)]
    //[Button(ButtonSizes.Large), GUIColor(0.4f, 0.8f, 1)]
    //public string SerializeGradient()
    //{
    //    return SerializeGradient(null);
    //}

    [TabGroup("Gradient Group", "Materials")]
    [Button(ButtonSizes.Large), GUIColor(0.4f, 0.8f, 1)]
    [HideIf("EmptyRecoverTexture")]
    public void RecoverTexture()
    {
        if (transformToCheck != null)
        {
            var skinnedMesh = transformToCheck.gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
            if (skinnedMesh != null && skinnedMesh.sharedMaterial != null)
            {
                Texture2D desiredTexture = null;
                oldTextureDictionary.TryGetValue(skinnedMesh.sharedMaterial, out desiredTexture);
                if(desiredTexture != null)
                    skinnedMesh.sharedMaterial.mainTexture = desiredTexture;
            }
            else
            {
                var mesh = transformToCheck.gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
                if (mesh != null && mesh.sharedMaterial != null)
                {
                    Texture2D desiredTexture = null;
                    oldTextureDictionary.TryGetValue(mesh.sharedMaterial, out desiredTexture);
                    if (desiredTexture != null)
                        mesh.sharedMaterial.mainTexture = desiredTexture;
                }
            }
        }

        foreach (Material materialToUpdate in updateMaterials)
        {
            Texture2D desiredTexture = null;
            oldTextureDictionary.TryGetValue(materialToUpdate, out desiredTexture);

            if (desiredTexture != null)
            {
                materialToUpdate.mainTexture = desiredTexture;
            }
        }
        oldTextureDictionary.Clear();
    }

    public string SerializeGradient()
    {
        return SerializeGradient(ref gradients);
    }

    public static string SerializeGradient(ref List<Gradient> _SerializeGradients)
    {
        string gradientString = "";
        for (int i = 0; i < _SerializeGradients.Count; i++)
        {
            if(i>0) gradientString += "/";
            //Debug.Log(gradients[i].colorKeys.Length);
            for (int g = 0; g < _SerializeGradients[i].colorKeys.Length; g++)
            {
                if (g > 0) gradientString += "#";
                gradientString += _SerializeGradients[i].colorKeys[g].color.r+";"+ _SerializeGradients[i].colorKeys[g].color.g + ";" + _SerializeGradients[i].colorKeys[g].color.b + ";" + _SerializeGradients[i].colorKeys[g].time;
            }
        }
        //Debug.Log(gradientString);
        return gradientString;
    }

    [TabGroup("Gradient Group", "Saving and Loading")]
    [ValueDropdown("TextFiles")]
    public TextAsset gradientTextAsset;
    public bool EmptyTexture
    {
        get { return TextureToSet == null; }
    }
    public bool EmptyName
    {
        get { return string.IsNullOrEmpty(textureName); }
    }
    public bool NullTextAsset
    {
        get { return gradientTextAsset == null; }
    }
    public bool EmptyRecoverTexture
    {
        get { return oldTextureDictionary.Count <= 0; }
    }

    string path = "Assets/GradientGenerator/Resources/EnvironmentTextures/";
    string pathTxt = "Assets/GradientGenerator/Resources/EnvironmentTexturesTxts/";
    
    [TabGroup("Gradient Group", "Saving and Loading")]
    [HorizontalGroup("Gradient Group/Saving and Loading/Split2", 0.5f)]
    [Button(ButtonSizes.Large), GUIColor(0.8f, 0.5f, 0.5f)]
    [DisableIf("NullTextAsset")]
    public void LoadGradient()
    {
        DeserializeFromText(gradientTextAsset.text, ref gradients);
    }

    public static List<Gradient> DeserializeFromText(string textToDeserialize, ref List<Gradient> gradientsToUse)
    {
        var gradientsDeserialize = textToDeserialize.Split('/');
        gradientsToUse.Clear();

        for (int i = 0; i < gradientsDeserialize.Length; i++)
        {
            var colorKeysDeserialize = gradientsDeserialize[i].Split('#');
            Gradient newGradient = new Gradient();

            GradientColorKey[] gck = new GradientColorKey[colorKeysDeserialize.Length];
                for (int y = 0; y < colorKeysDeserialize.Length; y++)
                {
                    var colorKeyInfo = colorKeysDeserialize[y].Split(';');
                    if (colorKeyInfo.Length >= 4)
                    {
                        Color newColor = new Color(GetFloat(colorKeyInfo[0]), GetFloat(colorKeyInfo[1]),
                            GetFloat(colorKeyInfo[2]), 1);
                        gck[y] = new GradientColorKey(newColor, GetFloat(colorKeyInfo[3]));
                    }
                    //Debug.Log("["+y.ColorString(newColor)+"] "+newColor.ColorString(newColor) + " time "+colorKeyInfo[3]);
                }
                newGradient.SetKeys(gck, newGradient.alphaKeys);
            gradientsToUse.Add(newGradient);
        }
        return gradientsToUse;
    }

    public static float GetFloat(string floatstring)
    {
        float newFloat = 0;
        float.TryParse(floatstring, out newFloat);
        return newFloat;
    }

    public static float Remap(float value, float from1, float to1, float from2, float to2)
    {
        return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
    }

    public Texture2D SaveTexture(Texture2D textureToSave, string _MainTexName, bool destroyafter = false)
    {
        if (textureToSave != null)
        {
            byte[] bytes = textureToSave.EncodeToPNG();
            FileStream stream = new FileStream(path + _MainTexName + ".png", FileMode.OpenOrCreate,
                FileAccess.Write);
            BinaryWriter writer = new BinaryWriter(stream);
            for (int i = 0; i < bytes.Length; i++)
            {
                writer.Write(bytes[i]);
            }
            writer.Close();
            stream.Close();
            Debug.Log("Saving");
            if (destroyafter) DestroyImmediate(textureToSave);
        }
        else
        {
            Debug.Log("Image not found " + textureToSave);
        }

        //Write some text to the test.txt file
        File.WriteAllText(pathTxt + _MainTexName + ".txt",SerializeGradient());
        AssetDatabase.ImportAsset(pathTxt + _MainTexName + ".txt");
        UpdateTextFilesAvailable();
        AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
        Texture2D _savedTexture =
            (Texture2D)AssetDatabase.LoadAssetAtPath(path + _MainTexName + ".png", typeof(Texture2D));

        if (_savedTexture)
        {
            return _savedTexture;
        }
        else
        {
            return textureToSave;
        }
    }

    public void UpdateTextFilesAvailable()
    {
        var allTextAssets = Resources.LoadAll<TextAsset>("EnvironmentTexturesTxts");
        TextFiles.Clear();
        foreach (var allTextAsset in allTextAssets)
        {
            TextFiles.Add(new ValueDropdownItem<TextAsset>(allTextAsset.name,allTextAsset));
        }
    }

    private static ValueDropdownList<TextAsset> TextFiles = new ValueDropdownList<TextAsset>()
    {
        
    };

}
