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

namespace VRWorldToolkit.Editor
{
    public enum OverrideCondition
    {
        Always,
        Smaller,
        Bigger
    }

    public enum DontOverrideWhen
    {
        Never,
        AlreadyDisabled,
        AlreadyEnabled
    }

    [Flags]
    public enum TextureTypeFilter
    {
        [InspectorName("Default")] Default = 1 << 0,
        [InspectorName("Normal map")] NormalMap = 1 << 1,
        [InspectorName("Editor GUI and Legacy GUI")] GUI = 1 << 2,
        [InspectorName("Cookie")] Cookie = 1 << 4,
        [InspectorName("Lightmap")] Lightmap = 1 << 6,
        [InspectorName("Cursor")] Cursor = 1 << 7,
        [InspectorName("Sprite (2D and UI)")] Sprite = 1 << 8,
        [InspectorName("Single Channel")] SingleChannel = 1 << 10,
        [InspectorName("Shadowmask")] Shadowmask = 1 << 11,
        [InspectorName("Directional Lightmap")] DirectionalLightmap = 1 << 12,
    }

    [Flags]
    public enum TextureShapeFilter
    {
        [InspectorName("2D")] Texture2D = 1 << 1,
        [InspectorName("Cube")] TextureCube = 1 << 2,
        [InspectorName("2D Array")] Texture2DArray = 1 << 3,
        [InspectorName("3D")] Texture3D = 1 << 4,
    }

    public enum TextureCompressionMode
    {
        None = 0,
        [InspectorName("Low Quality")] LowQuality = 1,
        [InspectorName("Normal Quality")] NormalQuality = 2,
        [InspectorName("High Quality")] HighQuality = 3
    }

    public static class MassTextureImporterExtensions
    {
        public static TextureCompressionMode ToTextureCompressionMode(this TextureImporterCompression compression)
        {
            return compression switch
            {
                TextureImporterCompression.Uncompressed => TextureCompressionMode.None,
                TextureImporterCompression.CompressedLQ => TextureCompressionMode.LowQuality,
                TextureImporterCompression.Compressed => TextureCompressionMode.NormalQuality,
                TextureImporterCompression.CompressedHQ => TextureCompressionMode.HighQuality,
                _ => TextureCompressionMode.NormalQuality
            };
        }

        public static TextureImporterCompression ToTextureImporterCompression(this TextureCompressionMode mode)
        {
            return mode switch
            {
                TextureCompressionMode.None => TextureImporterCompression.Uncompressed,
                TextureCompressionMode.LowQuality => TextureImporterCompression.CompressedLQ,
                TextureCompressionMode.NormalQuality => TextureImporterCompression.Compressed,
                TextureCompressionMode.HighQuality => TextureImporterCompression.CompressedHQ,
                _ => TextureImporterCompression.Compressed
            };
        }

        public static string GetDisplayName(this TextureImporterType type)
        {
            return type switch
            {
                TextureImporterType.Default => "Default",
                TextureImporterType.NormalMap => "Normal map",
                TextureImporterType.GUI => "Editor GUI and Legacy GUI",
                TextureImporterType.Cookie => "Cookie",
                TextureImporterType.Lightmap => "Lightmap",
                TextureImporterType.Cursor => "Cursor",
                TextureImporterType.Sprite => "Sprite (2D and UI)",
                TextureImporterType.SingleChannel => "Single Channel",
                TextureImporterType.Shadowmask => "Shadowmask",
                TextureImporterType.DirectionalLightmap => "Directional Lightmap",
                _ => type.ToString()
            };
        }

        public static string GetDisplayName(this TextureImporterShape shape)
        {
            return shape switch
            {
                TextureImporterShape.Texture2D => "2D",
                TextureImporterShape.TextureCube => "Cube",
                TextureImporterShape.Texture2DArray => "2D Array",
                TextureImporterShape.Texture3D => "3D",
                _ => shape.ToString()
            };
        }
    }

    public abstract class PlatformConfig
    {
        public abstract string DisplayName { get; }
        public abstract string PlatformKey { get; }
        public abstract string IconName { get; }
        public abstract int DefaultFormat { get; }
        public abstract int DrawFormatSelector(int currentFormat);
        public abstract bool SupportsCrunch(int format);
        public abstract bool SupportsCompressorQuality(int format);
    }

    public class StandalonePlatformConfig : PlatformConfig
    {
        public override string DisplayName => "Windows, Mac, Linux";
        public override string PlatformKey => "Standalone";
        public override string IconName => "BuildSettings.Standalone";
        public override int DefaultFormat => (int)TextureImporterFormat.DXT5Crunched;

        public override int DrawFormatSelector(int currentFormat)
            => Selectors.WindowsFormatIntPopup(currentFormat);

        public override bool SupportsCrunch(int format)
            => format == (int)TextureImporterFormat.DXT1Crunched ||
                format == (int)TextureImporterFormat.DXT5Crunched;

        public override bool SupportsCompressorQuality(int format)
            => format == (int)TextureImporterFormat.BC7 ||
                format == (int)TextureImporterFormat.BC6H;
    }

    public class AndroidPlatformConfig : PlatformConfig
    {
        public override string DisplayName => "Android";
        public override string PlatformKey => "Android";
        public override string IconName => "BuildSettings.Android";
        public override int DefaultFormat => (int)TextureImporterFormat.ASTC_6x6;

        public override int DrawFormatSelector(int currentFormat)
            => Selectors.MobileFormatIntPopup(currentFormat);

        public override bool SupportsCrunch(int format)
            => format == (int)TextureImporterFormat.ETC_RGB4Crunched ||
                format == (int)TextureImporterFormat.ETC2_RGBA8Crunched;

        public override bool SupportsCompressorQuality(int format)
            => format == (int)TextureImporterFormat.ASTC_12x12 ||
                format == (int)TextureImporterFormat.ASTC_10x10 ||
                format == (int)TextureImporterFormat.ASTC_8x8 ||
                format == (int)TextureImporterFormat.ASTC_6x6 ||
                format == (int)TextureImporterFormat.ASTC_5x5 ||
                format == (int)TextureImporterFormat.ASTC_4x4 ||
                format == (int)TextureImporterFormat.ETC2_RGBA8 ||
                format == (int)TextureImporterFormat.ETC2_RGB4_PUNCHTHROUGH_ALPHA ||
                format == (int)TextureImporterFormat.ETC2_RGB4;
    }

    public class iOSPlatformConfig : PlatformConfig
    {
        public override string DisplayName => "iOS";
        public override string PlatformKey => "iPhone";
        public override string IconName => "BuildSettings.iPhone";
        public override int DefaultFormat => (int)TextureImporterFormat.ASTC_6x6;

        public override int DrawFormatSelector(int currentFormat)
            => Selectors.MobileFormatIntPopup(currentFormat);

        public override bool SupportsCrunch(int format)
            => format == (int)TextureImporterFormat.ETC_RGB4Crunched ||
                format == (int)TextureImporterFormat.ETC2_RGBA8Crunched;

        public override bool SupportsCompressorQuality(int format)
            => format == (int)TextureImporterFormat.ASTC_12x12 ||
                format == (int)TextureImporterFormat.ASTC_10x10 ||
                format == (int)TextureImporterFormat.ASTC_8x8 ||
                format == (int)TextureImporterFormat.ASTC_6x6 ||
                format == (int)TextureImporterFormat.ASTC_5x5 ||
                format == (int)TextureImporterFormat.ASTC_4x4 ||
                format == (int)TextureImporterFormat.ETC2_RGBA8 ||
                format == (int)TextureImporterFormat.ETC2_RGB4_PUNCHTHROUGH_ALPHA ||
                format == (int)TextureImporterFormat.ETC2_RGB4;
    }

    public class PlatformTabManager
    {
        private int _selectedIndex;
        private readonly List<Tab> _tabs = new();
        private bool _iconsInitialized;

        private struct Tab
        {
            public string Label;
            public string IconName;
            public GUIContent Icon;
            public Action DrawContent;
        }

        public int SelectedIndex => _selectedIndex;

        public void AddTab(string label, string iconName, Action drawContent)
        {
            _tabs.Add(new Tab
            {
                Label = label,
                IconName = iconName,
                Icon = null,
                DrawContent = drawContent
            });
        }

        public void AddDefaultTab(Action drawContent)
        {
            _tabs.Insert(0, new Tab
            {
                Label = "Default",
                IconName = null,
                Icon = null,
                DrawContent = drawContent
            });
        }

        private void InitializeIconsIfNeeded()
        {
            if (_iconsInitialized) return;

            for (int i = 0; i < _tabs.Count; i++)
            {
                var tab = _tabs[i];

                if (string.IsNullOrEmpty(tab.IconName))
                {
                    tab.Icon = new GUIContent(tab.Label);
                }
                else
                {
                    var iconContent = EditorGUIUtility.IconContent(tab.IconName + ".Small");
                    tab.Icon = iconContent?.image != null
                        ? new GUIContent(iconContent.image, tab.Label)
                        : new GUIContent(tab.Label);
                }

                _tabs[i] = tab;
            }

            _iconsInitialized = true;
        }

        public void DrawTabs(Func<int, bool> isHighlighted = null)
        {
            InitializeIconsIfNeeded();

            EditorGUILayout.BeginHorizontal();

            for (int i = 0; i < _tabs.Count; i++)
            {
                DrawTab(i, isHighlighted?.Invoke(i) ?? false);
            }

            EditorGUILayout.EndHorizontal();

            _tabs[_selectedIndex].DrawContent?.Invoke();
        }

        private void DrawTab(int index, bool highlight)
        {
            bool isSelected = _selectedIndex == index;

            if (isSelected)
                GUI.backgroundColor = new Color(0.6f, 0.8f, 1f);

            if (GUILayout.Toggle(isSelected, _tabs[index].Icon, Styles.PlatformSelector))
                _selectedIndex = index;

            if (highlight)
            {
                var rect = GUILayoutUtility.GetLastRect();
                rect.width = 3;
                EditorGUI.DrawRect(rect, new Color(0.2f, 0.6f, 1f, 1f));
            }

            GUI.backgroundColor = Color.white;
        }
    }

    [Serializable]
    public class PlatformOverrideSettings
    {
        public bool Override;
        public bool DisableOverrides;
        public int MaxTextureSize = 2048;
        public OverrideCondition MaxSizeCondition = OverrideCondition.Bigger;
        public int Format;
        public bool UseCrunchCompression = true;
        public int CrunchQuality = 80;
        public TextureCompressionQuality TextureCompressionQuality = TextureCompressionQuality.Normal;

        private readonly PlatformConfig _config;

        public PlatformConfig Config => _config;
        public string DisplayName => _config.DisplayName;
        public string PlatformKey => _config.PlatformKey;
        public bool IsCrunchedFormat => _config.SupportsCrunch(Format);
        public bool HasCompressionQuality => _config.SupportsCompressorQuality(Format);

        public PlatformOverrideSettings(PlatformConfig config)
        {
            _config = config;
            Format = config.DefaultFormat;
        }

        public void DrawSettings()
        {
            using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
            {
                DisableOverrides = EditorGUILayout.ToggleLeft($"Disable Overrides for {DisplayName}", DisableOverrides);
                using (new EditorGUI.DisabledScope(DisableOverrides))
                {
                    Override = EditorGUILayout.ToggleLeft($"Override for {DisplayName}", Override);

                    using (new EditorGUI.DisabledScope(!Override))
                    using (new EditorGUI.IndentLevelScope())
                    {
                        MaxTextureSize = Selectors.MaxSizeIntPopup(MaxTextureSize);
                        using (new EditorGUI.IndentLevelScope())
                        {
                            MaxSizeCondition = (OverrideCondition)EditorGUILayout.EnumPopup("Override When", MaxSizeCondition);
                        }

                        Format = _config.DrawFormatSelector(Format);

                        if (IsCrunchedFormat)
                        {
                            UseCrunchCompression = EditorGUILayout.Toggle("Use Crunch Compression", UseCrunchCompression);
                            CrunchQuality = EditorGUILayout.IntSlider("Compressor Quality", CrunchQuality, 1, 100);
                        }

                        if (HasCompressionQuality)
                        {
                            TextureCompressionQuality = (TextureCompressionQuality)EditorGUILayout.EnumPopup("Compressor Quality", TextureCompressionQuality);
                        }
                    }
                }
            }
        }

        public bool ApplyTo(TextureImporter importer)
        {
            if (!Override && !DisableOverrides) return false;

            var settings = importer.GetPlatformTextureSettings(PlatformKey) ?? new TextureImporterPlatformSettings();
            bool changed = false;

            if (DisableOverrides)
            {
                if (settings.overridden)
                {
                    settings.overridden = false;
                    importer.SetPlatformTextureSettings(settings);
                    return true;
                }
                else
                {
                    return false;
                }
            }

            if (!settings.overridden)
            {
                settings.overridden = true;
                changed = true;
            }

            settings.name = PlatformKey;

            if (settings.format != (TextureImporterFormat)Format)
            {
                settings.format = (TextureImporterFormat)Format;
                changed = true;
            }

            int newMaxSize = MaxSizeCondition switch
            {
                OverrideCondition.Always => MaxTextureSize,
                OverrideCondition.Smaller when settings.maxTextureSize < MaxTextureSize => MaxTextureSize,
                OverrideCondition.Bigger when settings.maxTextureSize > MaxTextureSize => MaxTextureSize,
                _ => settings.maxTextureSize
            };

            if (settings.maxTextureSize != newMaxSize)
            {
                settings.maxTextureSize = newMaxSize;
                changed = true;
            }

            if (IsCrunchedFormat)
            {
                if (settings.crunchedCompression != UseCrunchCompression)
                {
                    settings.crunchedCompression = UseCrunchCompression;
                    changed = true;
                }

                if (UseCrunchCompression && settings.compressionQuality != CrunchQuality)
                {
                    settings.compressionQuality = CrunchQuality;
                    changed = true;
                }
            }

            if (HasCompressionQuality && settings.compressionQuality != (int)TextureCompressionQuality)
            {
                settings.compressionQuality = (int)TextureCompressionQuality;
                changed = true;
            }

            if (changed)
            {
                importer.SetPlatformTextureSettings(settings);
            }

            return changed;
        }
    }

    [Serializable]
    public class DefaultPlatformSettings
    {
        public bool ChangeMipMaps = true;
        public bool GenerateMipMaps = true;
        public bool StreamingMipMaps = true;

        public bool ChangeAniso;
        public int AnisoLevel = 1;
        public OverrideCondition AnisoCondition = OverrideCondition.Always;

        public bool ChangeMaxSize = true;
        public int MaxTextureSize = 2048;
        public OverrideCondition MaxSizeCondition = OverrideCondition.Bigger;

        public bool ChangeCompression = true;
        public TextureCompressionMode Compression = TextureCompressionMode.NormalQuality;
        public bool IgnoreUncompressed = true;

        public bool ChangeCrunch = true;
        public bool UseCrunch = true;
        public int CrunchQuality = 80;
        public OverrideCondition CrunchQualityCondition = OverrideCondition.Bigger;
        public DontOverrideWhen SkipCrunchWhen = DontOverrideWhen.AlreadyEnabled;

        public void DrawSettings()
        {
            using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
            {
                ChangeMaxSize = EditorGUILayout.Toggle("Change Max Size", ChangeMaxSize);
                using (new EditorGUI.DisabledScope(!ChangeMaxSize))
                using (new EditorGUI.IndentLevelScope())
                {
                    MaxTextureSize = Selectors.MaxSizeIntPopup(MaxTextureSize);
                    using (new EditorGUI.IndentLevelScope())
                    {
                        MaxSizeCondition = (OverrideCondition)EditorGUILayout.EnumPopup("Override When", MaxSizeCondition);
                    }
                }

                EditorGUILayout.Space();

                ChangeCompression = EditorGUILayout.Toggle("Change Compression", ChangeCompression);
                using (new EditorGUI.DisabledScope(!ChangeCompression))
                using (new EditorGUI.IndentLevelScope())
                {
                    Compression = (TextureCompressionMode)EditorGUILayout.EnumPopup("Compression", Compression);
                    IgnoreUncompressed = EditorGUILayout.Toggle("Ignore Uncompressed", IgnoreUncompressed);
                }

                EditorGUILayout.Space();

                using (new EditorGUI.DisabledScope(Compression == TextureCompressionMode.None))
                {
                    ChangeCrunch = EditorGUILayout.Toggle("Change Crunch Compression", ChangeCrunch);
                    using (new EditorGUI.DisabledScope(!ChangeCrunch))
                    using (new EditorGUI.IndentLevelScope())
                    {
                        UseCrunch = EditorGUILayout.Toggle("Use Crunch Compression", UseCrunch);
                        CrunchQuality = EditorGUILayout.IntSlider("Quality", CrunchQuality, 1, 100);
                        using (new EditorGUI.IndentLevelScope())
                        {
                            CrunchQualityCondition = (OverrideCondition)EditorGUILayout.EnumPopup("Override When", CrunchQualityCondition);
                        }
                        SkipCrunchWhen = (DontOverrideWhen)EditorGUILayout.EnumPopup("Don't Override When", SkipCrunchWhen);
                    }
                }
            }
        }

        public bool ApplyTo(TextureImporter importer)
        {
            bool changed = false;

            if (ChangeMipMaps)
            {
                if (importer.mipmapEnabled != GenerateMipMaps)
                {
                    importer.mipmapEnabled = GenerateMipMaps;
                    changed = true;
                }

                if (GenerateMipMaps && importer.streamingMipmaps != StreamingMipMaps)
                {
                    importer.streamingMipmaps = StreamingMipMaps;
                    changed = true;
                }
            }

            if (ChangeAniso)
            {
                int newAniso = AnisoCondition switch
                {
                    OverrideCondition.Always => AnisoLevel,
                    OverrideCondition.Smaller when importer.anisoLevel < AnisoLevel => AnisoLevel,
                    OverrideCondition.Bigger when importer.anisoLevel > AnisoLevel => AnisoLevel,
                    _ => importer.anisoLevel
                };

                if (importer.anisoLevel != newAniso)
                {
                    importer.anisoLevel = newAniso;
                    changed = true;
                }
            }

            if (ChangeMaxSize)
            {
                int newMaxSize = MaxSizeCondition switch
                {
                    OverrideCondition.Always => MaxTextureSize,
                    OverrideCondition.Smaller when importer.maxTextureSize < MaxTextureSize => MaxTextureSize,
                    OverrideCondition.Bigger when importer.maxTextureSize > MaxTextureSize => MaxTextureSize,
                    _ => importer.maxTextureSize
                };

                if (importer.maxTextureSize != newMaxSize)
                {
                    importer.maxTextureSize = newMaxSize;
                    changed = true;
                }
            }

            if (ChangeCompression)
            {
                if (!(IgnoreUncompressed && importer.textureCompression == TextureImporterCompression.Uncompressed))
                {
                    var newCompression = Compression.ToTextureImporterCompression();
                    if (importer.textureCompression != newCompression)
                    {
                        importer.textureCompression = newCompression;
                        changed = true;
                    }
                }
            }

            if (ChangeCrunch && importer.textureCompression != TextureImporterCompression.Uncompressed)
            {
                bool shouldSkip = SkipCrunchWhen switch
                {
                    DontOverrideWhen.AlreadyDisabled => !importer.crunchedCompression,
                    DontOverrideWhen.AlreadyEnabled => importer.crunchedCompression,
                    _ => false
                };

                if (!shouldSkip)
                {
                    if (importer.crunchedCompression != UseCrunch)
                    {
                        importer.crunchedCompression = UseCrunch;
                        changed = true;
                    }

                    if (UseCrunch)
                    {
                        int targetQuality = CrunchQualityCondition switch
                        {
                            OverrideCondition.Always => CrunchQuality,
                            OverrideCondition.Smaller when importer.compressionQuality < CrunchQuality => CrunchQuality,
                            OverrideCondition.Bigger when importer.compressionQuality > CrunchQuality => CrunchQuality,
                            _ => importer.compressionQuality
                        };

                        if (importer.compressionQuality != targetQuality)
                        {
                            importer.compressionQuality = targetQuality;
                            changed = true;
                        }
                    }
                }
            }

            return changed;
        }
    }

    public class TextureDetails
    {
        public readonly Dictionary<Texture, TextureImporter> TextureList = new();

        public IEnumerable<TextureImporter> GetImporters => TextureList.Values;

        public void AddTexture(TextureImporter textureImporter, Texture texture)
        {
            if (texture != null && textureImporter != null && !TextureList.ContainsKey(texture))
            {
                TextureList.Add(texture, textureImporter);
            }
        }
    }

    public class ImporterSettingsManager
    {
        public TextureTypeFilter TypeFilter = TextureTypeFilter.Default;
        public TextureShapeFilter ShapeFilter = TextureShapeFilter.Texture2D;

        private string _nameFilter;
        private Regex _nameRegex;
        private bool _nameRegexValid = true;

        private string _pathFilter;
        private Regex _pathRegex;
        private bool _pathRegexValid = true;

        public DefaultPlatformSettings DefaultSettings { get; } = new();

        private readonly Dictionary<string, PlatformOverrideSettings> _platformSettings;
        private readonly PlatformTabManager _tabController;

        public int MaxTextureSize => DefaultSettings.MaxTextureSize;
        public OverrideCondition OverrideMaxTextureSizeWhen => DefaultSettings.MaxSizeCondition;

        public ImporterSettingsManager()
        {
            _platformSettings = new Dictionary<string, PlatformOverrideSettings>
            {
                ["Standalone"] = new PlatformOverrideSettings(new StandalonePlatformConfig()),
                ["Android"] = new PlatformOverrideSettings(new AndroidPlatformConfig()),
                ["iPhone"] = new PlatformOverrideSettings(new iOSPlatformConfig())
            };

            _tabController = new PlatformTabManager();
            _tabController.AddDefaultTab(() => DefaultSettings.DrawSettings());

            foreach (var item in _platformSettings)
            {
                var settings = item.Value;
                _tabController.AddTab(
                    settings.DisplayName,
                    settings.Config.IconName,
                    () => settings.DrawSettings()
                );
            }
        }

        public PlatformOverrideSettings GetPlatformSettings(string platformKey)
            => _platformSettings.TryGetValue(platformKey, out var settings) ? settings : null;

        public static TextureTypeFilter ToTypeFilter(TextureImporterType type)
            => (TextureTypeFilter)(1 << (int)type);

        public static TextureShapeFilter ToShapeFilter(TextureImporterShape shape)
            => (TextureShapeFilter)(1 << (int)shape);

        public bool MatchesFilters(TextureImporter importer)
        {
            if (!string.IsNullOrEmpty(_nameFilter) && _nameRegex != null)
            {
                if (!_nameRegex.IsMatch(Path.GetFileName(importer.assetPath)))
                    return false;
            }

            if (!string.IsNullOrEmpty(_pathFilter) && _pathRegex != null)
            {
                if (!_pathRegex.IsMatch(importer.assetPath))
                    return false;
            }

            if ((TypeFilter & ToTypeFilter(importer.textureType)) == 0)
                return false;

            if ((ShapeFilter & ToShapeFilter(importer.textureShape)) == 0)
                return false;

            return true;
        }

        private void CompileNameRegex()
        {
            if (string.IsNullOrEmpty(_nameFilter))
            {
                _nameRegex = null;
                _nameRegexValid = true;
                return;
            }

            try
            {
                _nameRegex = new Regex(_nameFilter, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
                _nameRegexValid = true;
            }
            catch (ArgumentException)
            {
                _nameRegex = null;
                _nameRegexValid = false;
            }
        }

        private void CompilePathRegex()
        {
            if (string.IsNullOrEmpty(_pathFilter))
            {
                _pathRegex = null;
                _pathRegexValid = true;
                return;
            }

            try
            {
                _pathRegex = new Regex(_pathFilter, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
                _pathRegexValid = true;
            }
            catch (ArgumentException)
            {
                _pathRegex = null;
                _pathRegexValid = false;
            }
        }

        public void DrawSettings()
        {
            EditorGUIUtility.labelWidth = 150f;

            DrawFilters();
            EditorGUILayout.Space();
            DrawMipMapSettings();
            EditorGUILayout.Space();
            DrawAnisoSettings();
            EditorGUILayout.Space();
            DrawPlatformTabs();
        }

        private void DrawFilters()
        {
            GUILayout.Label("Filters", Styles.BoldWrap);
            using (new EditorGUI.IndentLevelScope())
            {
                TypeFilter = (TextureTypeFilter)EditorGUILayout.EnumFlagsField("Texture Type", TypeFilter);
                ShapeFilter = (TextureShapeFilter)EditorGUILayout.EnumFlagsField("Texture Shape", ShapeFilter);

                using (var check = new EditorGUI.ChangeCheckScope())
                {
                    _nameFilter = EditorGUILayout.TextField("Name Regex:", _nameFilter);
                    if (check.changed) CompileNameRegex();
                }

                if (!_nameRegexValid && !string.IsNullOrEmpty(_nameFilter))
                {
                    EditorGUILayout.Space();
                    EditorGUILayout.HelpBox("Invalid regex pattern for name", MessageType.Error);
                    EditorGUILayout.Space();
                }

                using (var check = new EditorGUI.ChangeCheckScope())
                {
                    _pathFilter = EditorGUILayout.TextField("Path Regex:", _pathFilter);
                    if (check.changed) CompilePathRegex();
                }

                if (!_pathRegexValid && !string.IsNullOrEmpty(_pathFilter))
                {
                    EditorGUILayout.Space();
                    EditorGUILayout.HelpBox("Invalid regex pattern for path", MessageType.Error);
                    EditorGUILayout.Space();
                }
            }
        }

        private void DrawMipMapSettings()
        {
            DefaultSettings.ChangeMipMaps = EditorGUILayout.Toggle("Change Mip Maps", DefaultSettings.ChangeMipMaps);
            using (new EditorGUI.IndentLevelScope())
            using (new EditorGUI.DisabledScope(!DefaultSettings.ChangeMipMaps))
            {
                DefaultSettings.GenerateMipMaps = EditorGUILayout.Toggle("Generate Mip Maps", DefaultSettings.GenerateMipMaps);
                using (new EditorGUI.DisabledScope(!DefaultSettings.GenerateMipMaps))
                {
                    DefaultSettings.StreamingMipMaps = EditorGUILayout.Toggle("Streaming Mip Maps", DefaultSettings.StreamingMipMaps);
                }
            }
        }

        private void DrawAnisoSettings()
        {
            DefaultSettings.ChangeAniso = EditorGUILayout.Toggle("Change Aniso Level", DefaultSettings.ChangeAniso);
            using (new EditorGUI.IndentLevelScope())
            using (new EditorGUI.DisabledScope(!DefaultSettings.ChangeAniso))
            {
                DefaultSettings.AnisoLevel = EditorGUILayout.IntSlider("Aniso Level", DefaultSettings.AnisoLevel, 0, 16);
                using (new EditorGUI.IndentLevelScope())
                {
                    DefaultSettings.AnisoCondition = (OverrideCondition)EditorGUILayout.EnumPopup("Override When", DefaultSettings.AnisoCondition);
                }
            }
        }

        private void DrawPlatformTabs()
        {
            EditorGUIUtility.labelWidth = 180f;
            _tabController.DrawTabs(index =>
            {
                if (index == 0) return false;

                string platformKey = index switch
                {
                    1 => "Standalone",
                    2 => "Android",
                    3 => "iPhone",
                    _ => null
                };

                return platformKey != null && _platformSettings.TryGetValue(platformKey, out var settings) && settings.Override;
            });
        }

        public void ProcessTextures(TextureDetails details)
        {
            int changedCount = 0;

            try
            {
                AssetDatabase.StartAssetEditing();

                var importers = details.GetImporters.ToList();
                int count = importers.Count;

                for (int i = 0; i < count; i++)
                {
                    var importer = importers[i];
                    if (importer == null) continue;

                    EditorUtility.DisplayProgressBar("Applying New Settings", importer.assetPath, (float)i / count);

                    if (!MatchesFilters(importer)) continue;

                    bool changed = DefaultSettings.ApplyTo(importer);

                    foreach (var platform in _platformSettings.Values)
                    {
                        changed |= platform.ApplyTo(importer);
                    }

                    if (changed)
                    {
                        changedCount++;
                        importer.SaveAndReimport();
                    }
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
                AssetDatabase.StopAssetEditing();
                EditorUtility.DisplayDialog("Changes Applied", $"Changes applied to {changedCount} Texture Importers.", "Ok");
            }
        }
    }

    public class TextureStats
    {
        private readonly Dictionary<Texture, TextureImporter> _textures;
        private readonly Func<TextureImporter, bool> _filterPredicate;

        private long? _totalSize;
        private long? _filteredSize;
        private int? _uncrunchedCount;
        private int? _filteredUncrunchedCount;
        private int? _filteredCount;

        public TextureStats(Dictionary<Texture, TextureImporter> textures, Func<TextureImporter, bool> filterPredicate)
        {
            _textures = textures;
            _filterPredicate = filterPredicate;
        }

        public int TotalCount => _textures.Count;
        public int FilteredCount => _filteredCount ??= _textures.Count(x => _filterPredicate(x.Value));
        public long TotalSize => _totalSize ??= _textures.Sum(x => EditorTextureUtil.GetStorageMemorySize(x.Key));

        public long FilteredSize => _filteredSize ??= _textures
            .Where(x => _filterPredicate(x.Value))
            .Sum(x => EditorTextureUtil.GetStorageMemorySize(x.Key));

        public int UncrunchedCount => _uncrunchedCount ??=
            _textures.Count(x => !x.Value.crunchedCompression || x.Value.textureCompression == TextureImporterCompression.Uncompressed);

        public int FilteredUncrunchedCount => _filteredUncrunchedCount ??=
            _textures.Count(x => (!x.Value.crunchedCompression || x.Value.textureCompression == TextureImporterCompression.Uncompressed) && _filterPredicate(x.Value));

        public void InvalidateFiltered()
        {
            _filteredSize = null;
            _filteredCount = null;
            _filteredUncrunchedCount = null;
        }

        public void InvalidateAll()
        {
            _totalSize = null;
            _uncrunchedCount = null;
            InvalidateFiltered();
        }
    }

    public class MassTextureImporter : EditorWindow
    {
        private TextureStats _stats;
        private TextureDetails _details;
        private ImporterSettingsManager _settingsManager;
        private bool _settingsChanged;
        private Vector2 _scrollPos;

        private TextureTreeView _textureTreeView;
        private SearchField _searchField;

        [NonSerialized] private bool initDone;
        [SerializeField] private TreeViewState _treeViewState;
        [SerializeField] private MultiColumnHeaderState _multiColumnHeaderState;
        [SerializeField] private MultiColumnHeader _multiColumnHeader;

        private const float SettingsPanelWidth = 450f;
        private const float MinWindowWidth = 1650f;
        private const float MinWindowHeight = 650f;

        [MenuItem("VRWorld Toolkit/Mass Texture Importer", false, 26)]
        public static void ShowWindow()
        {
            var window = GetWindow<MassTextureImporter>();
            window.titleContent = new GUIContent("Mass Texture Importer");
            window.minSize = new Vector2(MinWindowWidth, MinWindowHeight);
            window.Show();
        }

        private void OnEnable()
        {
            _settingsManager = new ImporterSettingsManager();
            _details = GetAllTexturesFromScene();
            _stats = new TextureStats(_details.TextureList, _settingsManager.MatchesFilters);
        }

        private void OnDisable()
        {
            if (_searchField != null && _textureTreeView != null)
            {
                _searchField.downOrUpArrowKeyPressed -= _textureTreeView.SetFocusAndEnsureSelectedItem;
            }

            _textureTreeView?.Cleanup();
            _details?.TextureList.Clear();
            _details = null;
            _stats = null;
            _textureTreeView = null;
        }

        private void OnGUI()
        {
            InitIfNeeded();

            using (new EditorGUILayout.HorizontalScope())
            {
                DrawSettingsPanel();
                DrawTextureTreeView();
            }
        }

        private void InitIfNeeded()
        {
            if (initDone) return;

            bool firstInit = _multiColumnHeaderState == null;
            var headerState = TextureTreeView.CreateDefaultMultiColumnHeaderState(EditorGUIUtility.currentViewWidth - 121);

            if (MultiColumnHeaderState.CanOverwriteSerializedFields(_multiColumnHeaderState, headerState))
            {
                MultiColumnHeaderState.OverwriteSerializedFields(_multiColumnHeaderState, headerState);
            }

            _multiColumnHeaderState = headerState;
            _multiColumnHeader = new MultiColumnHeader(headerState);

            if (firstInit)
            {
                _multiColumnHeader.ResizeToFit();
            }

            _treeViewState ??= new TreeViewState();

            _searchField = new SearchField();
            _textureTreeView = new TextureTreeView(_treeViewState, _multiColumnHeader, _details.TextureList, _settingsManager);
            _searchField.downOrUpArrowKeyPressed += _textureTreeView.SetFocusAndEnsureSelectedItem;

            initDone = true;
        }

        private void DrawSettingsPanel()
        {
            using (new EditorGUILayout.VerticalScope(GUILayout.Width(SettingsPanelWidth)))
            {
                DrawStats();

                using (var scrollView = new EditorGUILayout.ScrollViewScope(_scrollPos))
                {
                    _scrollPos = scrollView.scrollPosition;

                    using (var check = new EditorGUI.ChangeCheckScope())
                    {
                        _settingsManager.DrawSettings();

                        if (check.changed)
                        {
                            _settingsChanged = true;
                            _textureTreeView.Reload();
                            _stats.InvalidateFiltered();
                        }
                    }
                }

                DrawButtons();
            }
        }

        private void DrawStats()
        {
            EditorGUILayout.Space();
            using (new EditorGUILayout.HorizontalScope())
            {
                using (new EditorGUILayout.VerticalScope())
                {
                    GUILayout.Label("All Textures:", EditorStyles.boldLabel);
                    using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
                    {
                        GUILayout.Label($"<b>Texture Count:</b> {_stats.TotalCount}", Styles.LabelRichText);
                        GUILayout.Label($"<b>Uncrunched Count:</b> {_stats.UncrunchedCount}", Styles.LabelRichText);
                        GUILayout.Label($"<b>Storage Size:</b> {EditorUtility.FormatBytes(_stats.TotalSize)}", Styles.LabelRichText);
                    }
                }

                using (new EditorGUILayout.VerticalScope())
                {
                    GUILayout.Label("Filtered Textures:", EditorStyles.boldLabel);
                    using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
                    {
                        GUILayout.Label($"<b>Texture Count:</b> {_stats.FilteredCount}", Styles.LabelRichText);
                        GUILayout.Label($"<b>Uncrunched Count:</b> {_stats.FilteredUncrunchedCount}", Styles.LabelRichText);
                        GUILayout.Label($"<b>Storage Size:</b> {EditorUtility.FormatBytes(_stats.FilteredSize)}", Styles.LabelRichText);
                    }
                }
            }
        }

        private void DrawButtons()
        {
            GUILayout.Label("Get textures from:");

            using (new EditorGUILayout.HorizontalScope())
            {
                if (GUILayout.Button("Scene", GUILayout.Width(70), GUILayout.Height(20)))
                {
                    _details = GetAllTexturesFromScene();
                    _textureTreeView.SetTextures(_details.TextureList);
                    _stats = new TextureStats(_details.TextureList, _settingsManager.MatchesFilters);
                }

                if (GUILayout.Button("Assets", GUILayout.Width(70), GUILayout.Height(20)))
                {
                    _details = GetAllTexturesFromAssets();
                    _textureTreeView.SetTextures(_details.TextureList);
                    _stats = new TextureStats(_details.TextureList, _settingsManager.MatchesFilters);
                }

                GUILayout.FlexibleSpace();

                using (new EditorGUI.DisabledScope(!_settingsChanged))
                {
                    if (GUILayout.Button("Default", GUILayout.Width(70), GUILayout.Height(20)))
                    {
                        _settingsManager = new ImporterSettingsManager();
                        _settingsChanged = false;
                        _textureTreeView.SetSettingsManager(_settingsManager);
                    }
                }

                using (new EditorGUI.DisabledScope(_stats.FilteredCount < 1))
                {
                    if (GUILayout.Button("Apply", GUILayout.Width(70), GUILayout.Height(20)))
                    {
                        bool confirmed = EditorUtility.DisplayDialog("Process Importers?", $"About to process Texture Importer settings on {_stats.FilteredCount} textures. This can take a while depending on the number of textures with changes and their original size.\n\nDo you want to continue?", "Ok", "Cancel");

                        if (confirmed)
                        {
                            _settingsManager.ProcessTextures(_details);
                            _stats.InvalidateAll();
                            _textureTreeView.RefreshItems();
                        }
                    }
                }
            }
            EditorGUILayout.Space();
        }

        private void DrawTextureTreeView()
        {
            using (new EditorGUILayout.VerticalScope(GUILayout.ExpandWidth(true)))
            {
                using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
                {
                    if (GUILayout.Button("Refresh", EditorStyles.toolbarButton))
                    {
                        _textureTreeView.RefreshItems();
                    }

                    GUILayout.FlexibleSpace();

                    _textureTreeView.searchString = _searchField.OnToolbarGUI(_textureTreeView.searchString, GUILayout.Width(400));
                }

                var treeViewRect = EditorGUILayout.BeginVertical();
                _textureTreeView.OnGUI(treeViewRect);
                GUILayout.FlexibleSpace();
                EditorGUILayout.EndVertical();
            }
        }

        private static TextureDetails GetAllTexturesFromScene()
        {
            var details = new TextureDetails();
            var checkedMaterials = new HashSet<Material>();

            var allGameObjects = Resources.FindObjectsOfTypeAll<GameObject>();
            int totalCount = allGameObjects.Length;

            for (int i = 0; i < totalCount; i++)
            {
                var gameObject = allGameObjects[i];

                if (gameObject.hideFlags != HideFlags.None ||
                    EditorUtility.IsPersistent(gameObject.transform.root.gameObject))
                {
                    continue;
                }

                if (EditorUtility.DisplayCancelableProgressBar("Getting All Textures from the Scene", gameObject.name, (float)i / totalCount))
                {
                    EditorUtility.ClearProgressBar();
                    break;
                }

                var renderers = gameObject.GetComponents<Renderer>();

                foreach (var renderer in renderers)
                {
                    foreach (var material in renderer.sharedMaterials)
                    {
                        if (material == null || checkedMaterials.Contains(material))
                            continue;

                        checkedMaterials.Add(material);

                        var shader = material.shader;
                        if (shader == null) continue;
                        int propertyCount = ShaderUtil.GetPropertyCount(shader);

                        for (int j = 0; j < propertyCount; j++)
                        {
                            if (ShaderUtil.GetPropertyType(shader, j) != ShaderUtil.ShaderPropertyType.TexEnv)
                                continue;

                            var texture = material.GetTexture(ShaderUtil.GetPropertyName(shader, j));
                            if (texture == null) continue;

                            var path = AssetDatabase.GetAssetPath(texture);
                            if (!IsValidTexturePath(path)) continue;

                            var textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
                            if (textureImporter != null)
                            {
                                details.AddTexture(textureImporter, texture);
                            }
                        }
                    }
                }
            }

            EditorUtility.ClearProgressBar();
            return details;
        }

        private static bool IsValidTexturePath(string path)
        {
            if (string.IsNullOrEmpty(path) ||
                path.StartsWith("Resources/unity_builtin") ||
                path.StartsWith("Library/") ||
                 !path.StartsWith("Assets/")) return false;
            return true;
        }

        private static TextureDetails GetAllTexturesFromAssets()
        {
            var details = new TextureDetails();
            var assetGuids = AssetDatabase.FindAssets("t:texture2D", new[] { "Assets" });
            int totalCount = assetGuids.Length;

            for (int i = 0; i < totalCount; i++)
            {
                var path = AssetDatabase.GUIDToAssetPath(assetGuids[i]);

                if (EditorUtility.DisplayCancelableProgressBar("Getting All Textures from Assets", Path.GetFileName(path), (float)i / totalCount))
                {
                    break;
                }

                var texture = AssetDatabase.LoadAssetAtPath<Texture>(path);
                var textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;

                if (textureImporter != null)
                {
                    details.AddTexture(textureImporter, texture);
                }
            }

            EditorUtility.ClearProgressBar();
            return details;
        }
    }
}
