using System.IO;
using System.Text.RegularExpressions;

namespace TRX_Installer;

public class CueFile
{
    public readonly List<CueTrack> TrackList = new();

    public CueFile(string cueFilePath)
    {
        _cueFilePath = cueFilePath;
        string cueFileContent;
        using (TextReader cueReader = new StreamReader(cueFilePath))
        {
            cueFileContent = cueReader.ReadToEnd();
        }

        MatchCollection fileMatches = FileGroupRegex.Matches(cueFileContent);
        if (fileMatches.Count == 0)
        {
            throw new ApplicationException($"Could not parse {cueFilePath}: no tracks were found");
        }

        foreach (Match fileMatch in fileMatches.Cast<Match>())
        {
            string binFilePath = GetBinFilePath(fileMatch.Groups["name"].Value.Trim('"'));
            MatchCollection matches = TrackRegex.Matches(fileMatch.Groups["content"].Value);

            if (matches.Count == 0)
            {
                throw new ApplicationException($"Could not parse {cueFilePath}: no tracks were found");
            }

            CueTrack? track = null;
            CueTrack? prevTrack = null;
            foreach (Match trackMatch in matches.Cast<Match>())
            {
                track = new CueTrack(
                    binFilePath,
                    int.Parse(trackMatch.Groups["track"].Value),
                    trackMatch.Groups["mode"].Value,
                    trackMatch.Groups["time"].Value);

                if (prevTrack is not null)
                {
                    prevTrack.Stop = track.StartPosition - 1;
                    prevTrack.StopSector = track.StartSector;
                }
                TrackList.Add(track);
                prevTrack = track;
            }

            if (track is null)
            {
                return;
            }

            track.Stop = GetBinFileLength(binFilePath);
            track.StopSector = track.Stop / CueTrack.SectorLength;
        }
    }

    private static readonly Regex FileGroupRegex = new(
        @"^file\s+(?<name>""[^""]+""|[^""\s]+)\s+(?<mode>\w+)\s+(?<content>(.(?!^file))*)",
        RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);

    private static readonly Regex TrackRegex = new(
        @"track\s+?(?<track>\d+?)\s+?(?<mode>\S+?)[\s$]+?index\s+?\d+?\s+?(?<time>\S*)",
        RegexOptions.IgnoreCase | RegexOptions.Multiline);

    private readonly string _cueFilePath;

    private static long GetBinFileLength(string binFilePath)
    {
        FileInfo fileInfo = new(binFilePath);
        return fileInfo.Length;
    }

    private string GetBinFilePath(string name)
    {
        string cueDirectory = Path.GetDirectoryName(_cueFilePath)!;
        string result = Path.Combine(cueDirectory, Path.GetFileName(name));
        if (!File.Exists(result))
        {
            result = Path.Combine(cueDirectory, Path.GetFileNameWithoutExtension(_cueFilePath) + ".bin");
        }
        return result;
    }
}
