using System.Text;
using SabreTools.Data.Extensions;
using SabreTools.Data.Models.BSP;
using SabreTools.Text.Extensions;

namespace SabreTools.Wrappers
{
    public partial class BSP : 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("BSP Information:");
            builder.AppendLine("-------------------------");
            builder.AppendLine();

            Print(builder, Model.Header);
            PrintLumps(builder, Model);
        }

        private static void Print(StringBuilder builder, BspHeader? header)
        {
            builder.AppendLine("  Header Information:");
            builder.AppendLine("  -------------------------");
            if (header is null)
            {
                builder.AppendLine("  No header");
                builder.AppendLine();
                return;
            }

            builder.AppendLine(header.Version, "  Version");
            builder.AppendLine();
        }

        private static void PrintLumps(StringBuilder builder, BspFile? model)
        {
            builder.AppendLine("  Lumps Information:");
            builder.AppendLine("  -------------------------");
            if (model?.Header?.Lumps is null || model.Header.Lumps.Length == 0)
            {
                builder.AppendLine("  No lumps");
                builder.AppendLine();
                return;
            }

            for (int i = 0; i < model.Header.Lumps.Length; i++)
            {
                var lump = model.Header.Lumps[i];
                string specialLumpName = ((BspLumpType)i).FromBspLumpType();

                builder.AppendLine($"  Lump {i}{specialLumpName}");
                builder.AppendLine(lump.Offset, "    Offset");
                builder.AppendLine(lump.Length, "    Length");

                switch ((BspLumpType)i)
                {
                    case BspLumpType.LUMP_ENTITIES:
                        Print(builder, model.Entities);
                        break;
                    case BspLumpType.LUMP_PLANES:
                        Print(builder, model.PlanesLump);
                        break;
                    case BspLumpType.LUMP_TEXTURES:
                        Print(builder, model.TextureLump);
                        break;
                    case BspLumpType.LUMP_VERTICES:
                        Print(builder, model.VerticesLump);
                        break;
                    case BspLumpType.LUMP_VISIBILITY:
                        Print(builder, model.VisibilityLump);
                        break;
                    case BspLumpType.LUMP_NODES:
                        Print(builder, model.NodesLump);
                        break;
                    case BspLumpType.LUMP_TEXINFO:
                        Print(builder, model.TexinfoLump);
                        break;
                    case BspLumpType.LUMP_FACES:
                        Print(builder, model.FacesLump);
                        break;
                    case BspLumpType.LUMP_LIGHTING:
                        Print(builder, model.LightmapLump);
                        break;
                    case BspLumpType.LUMP_CLIPNODES:
                        Print(builder, model.ClipnodesLump);
                        break;
                    case BspLumpType.LUMP_LEAVES:
                        Print(builder, model.LeavesLump);
                        break;
                    case BspLumpType.LUMP_MARKSURFACES:
                        Print(builder, model.MarksurfacesLump);
                        break;
                    case BspLumpType.LUMP_EDGES:
                        Print(builder, model.EdgesLump);
                        break;
                    case BspLumpType.LUMP_SURFEDGES:
                        Print(builder, model.SurfedgesLump);
                        break;
                    case BspLumpType.LUMP_MODELS:
                        Print(builder, model.ModelsLump);
                        break;
                    default:
                        builder.AppendLine($"    Unsupported lump type: {(BspLumpType)i} (0x{i:X4})");
                        break;
                }
            }

            builder.AppendLine();
        }

        private static void Print(StringBuilder builder, EntitiesLump lump)
        {
            if (lump.Entities.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Entities.Length; i++)
            {
                var entity = lump.Entities[i];

                builder.AppendLine($"    Entity {i}:");
                if (entity.Attributes.Count == 0)
                {
                    builder.AppendLine("      No attributes");
                    continue;
                }

                for (int j = 0; j < entity.Attributes.Count; j++)
                {
                    var kvp = entity.Attributes[j];

                    builder.AppendLine($"      Attribute {j}: {kvp.Key}={kvp.Value}");
                }
            }
        }

        private static void Print(StringBuilder builder, PlanesLump lump)
        {
            if (lump.Planes.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Planes.Length; i++)
            {
                var plane = lump.Planes[i];

                builder.AppendLine($"    Plane {i}");
                builder.AppendLine($"      Normal vector: ({plane.NormalVector.X}, {plane.NormalVector.Y}, {plane.NormalVector.Z})");
                builder.AppendLine(plane.Distance, "      Distance");
                builder.AppendLine($"      Plane type: {plane.PlaneType} (0x{plane.PlaneType:X})");
            }
        }

        private static void Print(StringBuilder builder, TextureLump lump)
        {
            builder.AppendLine("    Texture Header:");
            builder.AppendLine(lump.Header.MipTextureCount, "      MipTexture count");
            builder.AppendLine(lump.Header.Offsets, "      Offsets");

            builder.AppendLine("    Textures:");
            if (lump.Textures.Length == 0)
            {
                builder.AppendLine("    No texture data");
                return;
            }

            for (int i = 0; i < lump.Textures.Length; i++)
            {
                var texture = lump.Textures[i];

                builder.AppendLine($"      Texture {i}");
                builder.AppendLine(texture.Name, "        Name");
                builder.AppendLine(texture.Width, "        Width");
                builder.AppendLine(texture.Height, "        Height");
                builder.AppendLine(texture.Offsets, "        Offsets");
            }
        }

        private static void Print(StringBuilder builder, VerticesLump lump)
        {
            if (lump.Vertices.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Vertices.Length; i++)
            {
                var vertex = lump.Vertices[i];
                builder.AppendLine($"    Vertex {i}: ({vertex.X}, {vertex.Y}, {vertex.Z})");
            }
        }

        private static void Print(StringBuilder builder, VisibilityLump lump)
        {
            builder.AppendLine(lump.NumClusters, "    Cluster count");
            builder.AppendLine("    Byte offsets skipped...");
        }

        private static void Print(StringBuilder builder, BspNodesLump lump)
        {
            if (lump.Nodes.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Nodes.Length; i++)
            {
                var node = lump.Nodes[i];

                builder.AppendLine($"    Node {i}");
                builder.AppendLine(node.Children, "      Children");
                builder.AppendLine(node.Mins, "      Mins");
                builder.AppendLine(node.Maxs, "      Maxs");
                builder.AppendLine(node.FirstFace, "      First face index");
                builder.AppendLine(node.FaceCount, "      Count of faces");
            }
        }

        private static void Print(StringBuilder builder, BspTexinfoLump lump)
        {
            if (lump.Texinfos.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Texinfos.Length; i++)
            {
                var texinfo = lump.Texinfos[i];

                builder.AppendLine($"    Texinfo {i}");
                builder.AppendLine($"      S-Vector: ({texinfo.SVector?.X}, {texinfo.SVector?.Y}, {texinfo.SVector?.Z})");
                builder.AppendLine(texinfo.TextureSShift, "      Texture shift in S direction");
                builder.AppendLine($"      T-Vector: ({texinfo.TVector?.X}, {texinfo.TVector?.Y}, {texinfo.TVector?.Z})");
                builder.AppendLine(texinfo.TextureTShift, "      Texture shift in T direction");
                builder.AppendLine(texinfo.MiptexIndex, "      Miptex index");
                builder.AppendLine($"      Flags: {texinfo.Flags} (0x{texinfo.Flags:X})");
            }
        }

        private static void Print(StringBuilder builder, BspFacesLump lump)
        {
            if (lump.Faces.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Faces.Length; i++)
            {
                var face = lump.Faces[i];

                builder.AppendLine($"    Face {i}");
                builder.AppendLine(face.PlaneIndex, "      Plane index");
                builder.AppendLine(face.PlaneSideCount, "      Plane side count");
                builder.AppendLine(face.FirstEdgeIndex, "      First surfedge index");
                builder.AppendLine(face.NumberOfEdges, "      Surfedge count");
                builder.AppendLine(face.TextureInfoIndex, "      Texture info index");
                builder.AppendLine(face.LightingStyles, "      Lighting styles");
                builder.AppendLine(face.LightmapOffset, "      Lightmap offset");
            }
        }

        private static void Print(StringBuilder builder, LightmapLump lump)
        {
            if (lump.Lightmap.Length == 0)
                builder.AppendLine("    No data");
            else
                builder.AppendLine("    Lightmap data skipped...");
        }

        private static void Print(StringBuilder builder, ClipnodesLump lump)
        {
            if (lump.Clipnodes.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Clipnodes.Length; i++)
            {
                var clipnode = lump.Clipnodes[i];

                builder.AppendLine($"    Clipnode {i}");
                builder.AppendLine(clipnode.PlaneIndex, "      Plane index");
                builder.AppendLine(clipnode.ChildrenIndices, "      Children indices");
            }
        }

        private static void Print(StringBuilder builder, BspLeavesLump lump)
        {
            if (lump.Leaves.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Leaves.Length; i++)
            {
                var leaf = lump.Leaves[i];

                builder.AppendLine($"    Leaf {i}");
                builder.AppendLine($"      Contents: {leaf.Contents} (0x{leaf.Contents:X})");
                builder.AppendLine(leaf.VisOffset, "      Visibility offset");
                builder.AppendLine(leaf.Mins, "      Mins");
                builder.AppendLine(leaf.Maxs, "      Maxs");
                builder.AppendLine(leaf.FirstMarkSurfaceIndex, "      First marksurface index");
                builder.AppendLine(leaf.MarkSurfacesCount, "      Marksurfaces count");
                builder.AppendLine(leaf.AmbientLevels, "      Ambient sound levels");
            }
        }

        private static void Print(StringBuilder builder, MarksurfacesLump lump)
        {
            if (lump.Marksurfaces.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Marksurfaces.Length; i++)
            {
                var marksurface = lump.Marksurfaces[i];
                builder.AppendLine(marksurface, $"    Marksurface {i}");
            }
        }

        private static void Print(StringBuilder builder, EdgesLump lump)
        {
            if (lump.Edges.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Edges.Length; i++)
            {
                var edge = lump.Edges[i];
                builder.AppendLine($"    Edge {i}");
                builder.AppendLine(edge.VertexIndices, "      Vertex indices");
            }
        }

        private static void Print(StringBuilder builder, SurfedgesLump lump)
        {
            if (lump.Surfedges.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Surfedges.Length; i++)
            {
                var surfedge = lump.Surfedges[i];
                builder.AppendLine(surfedge, $"    Surfedge {i}");
            }
        }

        private static void Print(StringBuilder builder, BspModelsLump lump)
        {
            if (lump.Models.Length == 0)
            {
                builder.AppendLine("    No data");
                return;
            }

            for (int i = 0; i < lump.Models.Length; i++)
            {
                var bmodel = lump.Models[i];

                builder.AppendLine($"    Model {i}");
                builder.AppendLine($"      Mins: {bmodel.Mins?.X}, {bmodel.Mins?.Y}, {bmodel.Mins?.Z}");
                builder.AppendLine($"      Maxs: {bmodel.Maxs?.X}, {bmodel.Maxs?.Y}, {bmodel.Maxs?.Z}");
                builder.AppendLine($"      Origin vector: {bmodel.OriginVector?.X}, {bmodel.OriginVector?.Y}, {bmodel.OriginVector?.Z}");
                builder.AppendLine(bmodel.HeadnodesIndex, "      Headnodes index");
                builder.AppendLine(bmodel.VisLeafsCount, "      ??? (VisLeafsCount)");
                builder.AppendLine(bmodel.FirstFaceIndex, "      First face index");
                builder.AppendLine(bmodel.FacesCount, "      Faces count");
            }
        }
    }
}
