using OtterGui;
using OtterGui.Compression;
using OtterGui.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;

namespace Penumbra.Mods.Editor;

public partial class MdlMaterialEditor(ModFileCollection files) : IService
{
    [GeneratedRegex(@"/mt_c(?'RaceCode'\d{4})b0001_(?'Suffix'.*?)\.mtrl", RegexOptions.ExplicitCapture | RegexOptions.NonBacktracking)]
    private static partial Regex MaterialRegex();

    private readonly List<ModelMaterialInfo> _modelFiles = [];

    public IReadOnlyList<ModelMaterialInfo> ModelFiles
        => _modelFiles;

    public void SaveAllModels(FileCompactor compactor)
    {
        foreach (var info in _modelFiles)
            info.Save(compactor);
    }

    public void RestoreAllModels()
    {
        foreach (var info in _modelFiles)
            info.Restore();
    }

    public void Clear()
    {
        _modelFiles.Clear();
    }

    /// <summary>
    /// Go through the currently loaded files and replace all appropriate suffices.
    /// Does nothing if toSuffix is invalid.
    /// If raceCode is Unknown, apply to all raceCodes.
    /// If fromSuffix is empty, apply to all suffices.
    /// </summary>
    public void ReplaceAllMaterials(string toSuffix, string fromSuffix = "", GenderRace raceCode = GenderRace.Unknown)
    {
        if (!ValidString(toSuffix))
            return;

        foreach (var info in _modelFiles)
        {
            for (var i = 0; i < info.Count; ++i)
            {
                var (_, def) = info[i];
                var match = MaterialRegex().Match(def);
                if (match.Success
                 && (raceCode == GenderRace.Unknown || raceCode.ToRaceCode() == match.Groups["RaceCode"].Value)
                 && (fromSuffix.Length == 0 || fromSuffix == match.Groups["Suffix"].Value))
                    info.SetMaterial($"/mt_c{match.Groups["RaceCode"].Value}b0001_{toSuffix}.mtrl", i);
            }
        }
    }

    /// Non-ASCII encoding is not supported.
    public static bool ValidString(string to)
        => to.Length != 0
         && to.Length < 16
         && Encoding.UTF8.GetByteCount(to) == to.Length;

    /// <summary> Find all model files in the mod that contain skin materials. </summary>
    public void ScanModels(Mod mod)
    {
        _modelFiles.Clear();
        foreach (var file in files.Mdl)
        {
            try
            {
                var bytes   = File.ReadAllBytes(file.File.FullName);
                var mdlFile = new MdlFile(bytes);
                var materials = mdlFile.Materials.WithIndex().Where(p => MaterialRegex().IsMatch((string)p.Item1))
                    .Select(p => p.Item2).ToArray();
                if (materials.Length > 0)
                    _modelFiles.Add(new ModelMaterialInfo(file.File, mdlFile, materials));
            }
            catch (Exception e)
            {
                Penumbra.Log.Error($"Unexpected error scanning {mod.Name}'s {file.File.FullName} for materials:\n{e}");
            }
        }
    }
}
