using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.Data.Models.DosCenter;
using SabreTools.Text.ClrMamePro;
using SabreTools.Text.Extensions;

namespace SabreTools.Serialization.Readers
{
    public class DosCenter : BaseBinaryReader<MetadataFile>
    {
        /// <inheritdoc/>
        public override MetadataFile? Deserialize(Stream? data)
        {
            // If the data is invalid
            if (data is null || !data.CanRead)
                return null;

            try
            {
                // Setup the reader and output
                var reader = new Reader(data, Encoding.UTF8) { DosCenter = true };
                var dat = new MetadataFile();

                // Loop through and parse out the values
                string? lastTopLevel = reader.TopLevel;

                Game? game = null;
                var games = new List<Game>();
                var files = new List<Data.Models.DosCenter.File>();

                while (!reader.EndOfStream)
                {
                    // If we have no next line
                    if (!reader.ReadNextLine())
                        break;

                    // Ignore certain row types
                    switch (reader.RowType)
                    {
                        case CmpRowType.None:
                        case CmpRowType.Comment:
                            continue;
                        case CmpRowType.EndTopLevel:
                            switch (lastTopLevel)
                            {
                                case "game":
                                    if (game is not null)
                                    {
                                        game.File = [.. files];
                                        games.Add(game);
                                    }

                                    game = null;
                                    files.Clear();
                                    break;

                                default:
                                    // TODO: Log invalid values
                                    break;
                            }

                            continue;

                        case CmpRowType.TopLevel:
                        case CmpRowType.Standalone:
                        case CmpRowType.Internal:
                            break;

                        default:
                            // TODO: Log invalid values
                            break;
                    }

                    // If we're at the root
                    if (reader.RowType == CmpRowType.TopLevel)
                    {
                        lastTopLevel = reader.TopLevel;
                        switch (reader.TopLevel)
                        {
                            case "doscenter":
                                dat.DosCenter = new Data.Models.DosCenter.DosCenter();
                                break;
                            case "game":
                                game = new Game();
                                break;
                            default:
                                // TODO: Log invalid values
                                break;
                        }
                    }

                    // If we're in the doscenter block
                    else if (reader.TopLevel == "doscenter" && reader.RowType == CmpRowType.Standalone)
                    {
                        // Create the block if we haven't already
                        dat.DosCenter ??= new Data.Models.DosCenter.DosCenter();

                        switch (reader.Standalone?.Key?.ToLowerInvariant())
                        {
                            case "name:":
                                dat.DosCenter.Name = reader.Standalone?.Value;
                                break;
                            case "description:":
                                dat.DosCenter.Description = reader.Standalone?.Value;
                                break;
                            case "version:":
                                dat.DosCenter.Version = reader.Standalone?.Value;
                                break;
                            case "date:":
                                dat.DosCenter.Date = reader.Standalone?.Value;
                                break;
                            case "author:":
                                dat.DosCenter.Author = reader.Standalone?.Value;
                                break;
                            case "homepage:":
                                dat.DosCenter.Homepage = reader.Standalone?.Value;
                                break;
                            case "comment:":
                                dat.DosCenter.Comment = reader.Standalone?.Value;
                                break;
                            default:
                                // TODO: Log invalid values
                                break;
                        }
                    }

                    // If we're in a game block
                    else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Standalone)
                    {
                        // Create the block if we haven't already
                        game ??= new Game();

                        switch (reader.Standalone?.Key?.ToLowerInvariant())
                        {
                            case "name":
                                game.Name = reader.Standalone?.Value;
                                break;
                            default:
                                // TODO: Log invalid values
                                break;
                        }
                    }

                    // If we're in a file block
                    else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Internal)
                    {
                        // If we have an unknown type, log it
                        if (reader.InternalName != "file")
                            continue;

                        // Create the file and add to the list
                        var file = CreateFile(reader);
                        if (file is not null)
                            files.Add(file);
                    }
                }

                // Add extra pieces and return
                if (games.Count > 0)
                {
                    dat.Game = [.. games];
                    return dat;
                }

                return null;
            }
            catch
            {
                // Ignore the actual error
                return null;
            }
        }

        /// <summary>
        /// Create a File object from the current reader context
        /// </summary>
        /// <param name="reader">ClrMameProReader representing the metadata file</param>
        /// <returns>File object created from the reader context</returns>
        private Data.Models.DosCenter.File? CreateFile(Reader reader)
        {
            if (reader.Internal is null)
                return null;

            var file = new Data.Models.DosCenter.File();
            foreach (var kvp in reader.Internal)
            {
                switch (kvp.Key?.ToLowerInvariant())
                {
                    case "name":
                        file.Name = kvp.Value;
                        break;
                    case "size":
                        file.Size = NumberHelper.ConvertToInt64(kvp.Value);
                        break;
                    case "crc":
                        file.CRC = kvp.Value;
                        break;
                    case "sha1":
                        file.SHA1 = kvp.Value;
                        break;
                    case "date":
                        file.Date = kvp.Value;
                        break;
                    default:
                        if (Debug) Console.Error.WriteLine($"'{kvp.Key}' is an unrecognized field");
                        break;
                }
            }

            return file;
        }
    }
}
