using System.Collections.Generic;
using System.Text;
using SabreTools.Data.Models.GCF;
using SabreTools.Text.Extensions;

namespace SabreTools.Wrappers
{
    public partial class GCF : IPrintable
    {
#if NETCOREAPP
        /// <inheritdoc/>
        public string ExportJSON(bool recursive) => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions);
#else
        /// <inheritdoc/>
        public string ExportJSON(bool recursive) => Newtonsoft.Json.JsonConvert.SerializeObject(Model, _jsonSerializerOptions);
#endif

        /// <inheritdoc/>
        public void PrintInformation(StringBuilder builder, bool recursive)
        {
            builder.AppendLine("GCF Information:");
            builder.AppendLine("-------------------------");
            builder.AppendLine();

            // Header
            Print(builder, Model.Header);

            // Block Entries
            Print(builder, Model.BlockEntryHeader);
            Print(builder, Model.BlockEntries);

            // Fragmentation Maps
            Print(builder, Model.FragmentationMapHeader);
            Print(builder, Model.FragmentationMaps);

            // Block Entry Maps
            Print(builder, Model.BlockEntryMapHeader);
            Print(builder, Model.BlockEntryMaps);

            // Directory and Directory Maps
            Print(builder, Model.DirectoryHeader);
            Print(builder, Model.DirectoryEntries, Model.DirectoryNames);
            // TODO: Should we print out the entire string table?
            Print(builder, Model.DirectoryInfo1Entries);
            Print(builder, Model.DirectoryInfo2Entries);
            Print(builder, Model.DirectoryCopyEntries);
            Print(builder, Model.DirectoryLocalEntries);
            Print(builder, Model.DirectoryMapHeader);
            Print(builder, Model.DirectoryMapEntries);

            // Checksums and Checksum Maps
            Print(builder, Model.ChecksumHeader);
            Print(builder, Model.ChecksumMapHeader);
            Print(builder, Model.ChecksumMapEntries);
            Print(builder, Model.ChecksumEntries);

            // Data Blocks
            Print(builder, Model.DataBlockHeader);
        }

        private static void Print(StringBuilder builder, Header header)
        {
            builder.AppendLine("  Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.MajorVersion, "  Major version");
            builder.AppendLine(header.MinorVersion, "  Minor version");
            builder.AppendLine(header.CacheID, "  Cache ID");
            builder.AppendLine(header.LastVersionPlayed, "  Last version played");
            builder.AppendLine(header.Dummy1, "  Dummy 1");
            builder.AppendLine(header.Dummy2, "  Dummy 2");
            builder.AppendLine(header.FileSize, "  File size");
            builder.AppendLine(header.BlockSize, "  Block size");
            builder.AppendLine(header.BlockCount, "  Block count");
            builder.AppendLine(header.Dummy3, "  Dummy 3");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, BlockEntryHeader header)
        {
            builder.AppendLine("  Block Entry Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.BlockCount, "  Block count");
            builder.AppendLine(header.BlocksUsed, "  Blocks used");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.Dummy1, "  Dummy 1");
            builder.AppendLine(header.Dummy2, "  Dummy 2");
            builder.AppendLine(header.Dummy3, "  Dummy 3");
            builder.AppendLine(header.Dummy4, "  Dummy 4");
            builder.AppendLine(header.Checksum, "  Checksum");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, BlockEntry[] entries)
        {
            builder.AppendLine("  Block Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No block entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Block Entry {i}");
                builder.AppendLine(entry.EntryFlags, "    Entry flags");
                builder.AppendLine(entry.FileDataOffset, "    File data offset");
                builder.AppendLine(entry.FileDataSize, "    File data size");
                builder.AppendLine(entry.FirstDataBlockIndex, "    First data block index");
                builder.AppendLine(entry.NextBlockEntryIndex, "    Next block entry index");
                builder.AppendLine(entry.PreviousBlockEntryIndex, "    Previous block entry index");
                builder.AppendLine(entry.DirectoryIndex, "    Directory index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, FragmentationMapHeader header)
        {
            builder.AppendLine("  Fragmentation Map Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.BlockCount, "  Block count");
            builder.AppendLine(header.FirstUnusedEntry, "  First unused entry");
            builder.AppendLine(header.Terminator, "  Terminator");
            builder.AppendLine(header.Checksum, "  Checksum");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, FragmentationMap[] entries)
        {
            builder.AppendLine("  Fragmentation Maps Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No fragmentation maps");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Fragmentation Map {i}");
                builder.AppendLine(entry.NextDataBlockIndex, "    Next data block index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, BlockEntryMapHeader header)
        {
            builder.AppendLine("  Block Entry Map Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.BlockCount, "  Block count");
            builder.AppendLine(header.FirstBlockEntryIndex, "  First block entry index");
            builder.AppendLine(header.LastBlockEntryIndex, "  Last block entry index");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.Checksum, "  Checksum");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, BlockEntryMap[] entries)
        {
            builder.AppendLine("  Block Entry Maps Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No block entry maps");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Block Entry Map {i}");
                builder.AppendLine(entry.PreviousBlockEntryIndex, "    Previous data block index");
                builder.AppendLine(entry.NextBlockEntryIndex, "    Next data block index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryHeader header)
        {
            builder.AppendLine("  Directory Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.CacheID, "  Cache ID");
            builder.AppendLine(header.LastVersionPlayed, "  Last version played");
            builder.AppendLine(header.ItemCount, "  Item count");
            builder.AppendLine(header.FileCount, "  File count");
            builder.AppendLine(header.Dummy1, "  Dummy 1");
            builder.AppendLine(header.DirectorySize, "  Directory size");
            builder.AppendLine(header.NameSize, "  Name size");
            builder.AppendLine(header.Info1Count, "  Info 1 count");
            builder.AppendLine(header.CopyCount, "  Copy count");
            builder.AppendLine(header.LocalCount, "  Local count");
            builder.AppendLine(header.Dummy2, "  Dummy 2");
            builder.AppendLine(header.Dummy3, "  Dummy 3");
            builder.AppendLine(header.Checksum, "  Checksum");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryEntry[] entries, Dictionary<long, string?> entryNames)
        {
            builder.AppendLine("  Directory Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Entry {i}");
                builder.AppendLine(entry.NameOffset, "    Name offset");
                builder.AppendLine(entryNames![entry.NameOffset], "    Name");
                builder.AppendLine(entry.ItemSize, "    Item size");
                builder.AppendLine(entry.ChecksumIndex, "    Checksum index");
                builder.AppendLine($"    Directory flags: {entry.DirectoryFlags} (0x{entry.DirectoryFlags:X})");
                builder.AppendLine(entry.ParentIndex, "    Parent index");
                builder.AppendLine(entry.NextIndex, "    Next index");
                builder.AppendLine(entry.FirstIndex, "    First index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryInfo1Entry[] entries)
        {
            builder.AppendLine("  Directory Info 1 Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory info 1 entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Info 1 Entry {i}");
                builder.AppendLine(entry.Dummy0, "    Dummy 0");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryInfo2Entry[] entries)
        {
            builder.AppendLine("  Directory Info 2 Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory info 2 entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Info 2 Entry {i}");
                builder.AppendLine(entry.Dummy0, "    Dummy 0");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryCopyEntry[] entries)
        {
            builder.AppendLine("  Directory Copy Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory copy entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Copy Entry {i}");
                builder.AppendLine(entry.DirectoryIndex, "    Directory index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryLocalEntry[] entries)
        {
            builder.AppendLine("  Directory Local Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory local entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Local Entry {i}");
                builder.AppendLine(entry.DirectoryIndex, "    Directory index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryMapHeader header)
        {
            builder.AppendLine("  Directory Map Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.Dummy1, "  Dummy 1");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DirectoryMapEntry[] entries)
        {
            builder.AppendLine("  Directory Map Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No directory map entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Directory Map Entry {i}");
                builder.AppendLine(entry.FirstBlockIndex, "    First block index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, ChecksumHeader header)
        {
            builder.AppendLine("  Checksum Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.ChecksumSize, "  Checksum size");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, ChecksumMapHeader header)
        {
            builder.AppendLine("  Checksum Map Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.Dummy0, "  Dummy 0");
            builder.AppendLine(header.Dummy1, "  Dummy 1");
            builder.AppendLine(header.ItemCount, "  Item count");
            builder.AppendLine(header.ChecksumCount, "  Checksum count");
            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, ChecksumMapEntry[] entries)
        {
            builder.AppendLine("  Checksum Map Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No checksum map entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Checksum Map Entry {i}");
                builder.AppendLine(entry.ChecksumCount, "    Checksum count");
                builder.AppendLine(entry.FirstChecksumIndex, "    First checksum index");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, ChecksumEntry[] entries)
        {
            builder.AppendLine("  Checksum Entries Information:");
            builder.AppendLine("  -------------------------");
            if (entries.Length == 0)
            {
                builder.AppendLine("  No checksum entries");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < entries.Length; i++)
            {
                var entry = entries[i];

                builder.AppendLine($"  Checksum Entry {i}");
                builder.AppendLine(entry.Checksum, "    Checksum");
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, DataBlockHeader header)
        {
            builder.AppendLine("  Data Block Header Information:");
            builder.AppendLine("  -------------------------");
            builder.AppendLine(header.LastVersionPlayed, "  Last version played");
            builder.AppendLine(header.BlockCount, "  Block count");
            builder.AppendLine(header.BlockSize, "  Block size");
            builder.AppendLine(header.FirstBlockOffset, "  First block offset");
            builder.AppendLine(header.BlocksUsed, "  Blocks used");
            builder.AppendLine(header.Checksum, "  Checksum");
            builder.AppendLine();
        }
    }
}
