using System.Text.RegularExpressions;

namespace GsaEditor.Core.IO;

/// <summary>
/// Represents a single entry parsed from a .idx NML index file.
/// </summary>
public class GsaIndexEntry
{
    /// <summary>
    /// The file alias (relative path) of the entry.
    /// </summary>
    public string Id { get; set; } = string.Empty;

    /// <summary>
    /// Compression method: 0 = raw, 1 = zlib.
    /// </summary>
    public int Method { get; set; }

    /// <summary>
    /// Original (decompressed) size in bytes.
    /// </summary>
    public uint Length { get; set; }

    /// <summary>
    /// Compressed size in bytes.
    /// </summary>
    public uint CompressedLength { get; set; }

    /// <summary>
    /// Byte offset of the data block within the archive.
    /// </summary>
    public long Offset { get; set; }
}

/// <summary>
/// Parses .idx NML index files into a list of <see cref="GsaIndexEntry"/>.
/// NML is the engine's own tag syntax (&lt;Name=value&gt; blocks, see <see cref="GsaIndexWriter"/>),
/// not standard XML.
/// </summary>
public static class GsaIndexReader
{
    private static readonly Regex EntryRegex = new(
        @"<Entry=(?<body>.*?)\n\s*>",
        RegexOptions.Singleline | RegexOptions.Compiled);

    private static readonly Regex TagRegex = new(
        @"<(?<name>\w+)=""?(?<value>[^"">]*)""?>",
        RegexOptions.Compiled);

    /// <summary>
    /// Reads index entries from the specified .idx file.
    /// </summary>
    /// <param name="filePath">The path to the .idx file.</param>
    /// <returns>A list of parsed index entries.</returns>
    public static List<GsaIndexEntry> Read(string filePath)
    {
        var text = File.ReadAllText(filePath);
        var entries = new List<GsaIndexEntry>();

        foreach (Match entryMatch in EntryRegex.Matches(text))
        {
            var entry = new GsaIndexEntry();

            foreach (Match tag in TagRegex.Matches(entryMatch.Groups["body"].Value))
            {
                var value = tag.Groups["value"].Value;
                switch (tag.Groups["name"].Value)
                {
                    case "ID":
                        entry.Id = value;
                        break;
                    case "Method":
                        entry.Method = value.Equals("Zlib", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
                        break;
                    case "Len":
                        entry.Length = uint.TryParse(value, out var l) ? l : 0;
                        break;
                    case "CompLen":
                        entry.CompressedLength = uint.TryParse(value, out var cl) ? cl : 0;
                        break;
                    case "Offset":
                        entry.Offset = long.TryParse(value, out var o) ? o : 0;
                        break;
                }
            }

            entries.Add(entry);
        }

        return entries;
    }
}
