using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Spine;

// Офлайн-экспортёр: читает Spine .skel.bytes+.atlas через spine-csharp и
// выгружает геометрию (кости setup + привязки с мировыми вершинами/UV/треуг/весами) в свой JSON.
// Использование: spineconv <base_path_без_расширения> <out.json>

class NullTex : TextureLoader
{
    public void Load(AtlasPage page, string path) { }
    public void Unload(object texture) { }
}

class Program
{
    static void Main(string[] args)
    {
        string baseP = args[0];
        string outPath = args[1];

        var atlas = new Atlas(baseP + ".atlas.txt", new NullTex());
        var loader = new AtlasAttachmentLoader(atlas);
        var binary = new SkeletonBinary(loader) { Scale = 1f };
        SkeletonData data = binary.ReadSkeletonData(baseP + ".skel.bytes");

        var skeleton = new Skeleton(data);
        skeleton.SetToSetupPose();
        skeleton.UpdateWorldTransform(Skeleton.Physics.Update);

        var boneIndex = new Dictionary<BoneData, int>();
        var bonesOut = new List<object>();
        var bones = data.Bones;
        for (int i = 0; i < bones.Count; i++)
        {
            var b = bones.Items[i];
            boneIndex[b] = i;
            bonesOut.Add(new
            {
                name = b.Name,
                parent = b.Parent != null ? b.Parent.Name : null,
                x = b.X, y = b.Y, rotation = b.Rotation,
                scaleX = b.ScaleX, scaleY = b.ScaleY,
                shearX = b.ShearX, shearY = b.ShearY, length = b.Length
            });
        }

        var attsOut = new List<object>();
        var pages = new HashSet<string>();
        var drawOrder = skeleton.DrawOrder;
        for (int d = 0; d < drawOrder.Count; d++)
        {
            Slot slot = drawOrder.Items[d];
            Attachment att = slot.Attachment;
            if (att == null) continue;
            int sBone = boneIndex[slot.Data.BoneData];
            var sd = slot.Data;
            float[] color = { sd.R, sd.G, sd.B, sd.A };

            if (att is RegionAttachment ra)
            {
                float[] wv = new float[8];
                ra.ComputeWorldVertices(slot, wv, 0, 2);
                var reg = (AtlasRegion)ra.Region;
                pages.Add(reg.page.name);
                var weights = new List<object>();
                for (int k = 0; k < 4; k++)
                    weights.Add(new object[] { new object[] { sBone, 1f } });
                attsOut.Add(new
                {
                    slot = sd.Name, name = ra.Name, type = "region", page = reg.page.name,
                    world = wv, uv = ra.UVs, triangles = new int[] { 0, 1, 2, 2, 3, 0 },
                    weights, color
                });
            }
            else if (att is MeshAttachment ma)
            {
                int n = ma.WorldVerticesLength;
                float[] wv = new float[n];
                ma.ComputeWorldVertices(slot, 0, n, wv, 0, 2);
                var reg = (AtlasRegion)ma.Region;
                pages.Add(reg.page.name);
                attsOut.Add(new
                {
                    slot = sd.Name, name = ma.Name, type = "mesh", page = reg.page.name,
                    world = wv, uv = ma.UVs, triangles = ma.Triangles,
                    weights = ParseWeights(ma, sBone), color
                });
            }
        }

        var outObj = new { bones = bonesOut, pages = new List<string>(pages), attachments = attsOut };
        File.WriteAllText(outPath, JsonSerializer.Serialize(outObj));
        int meshes = 0, regions = 0, weighted = 0;
        foreach (var s in drawOrder)
        {
            if (s.Attachment is MeshAttachment m) { meshes++; if (m.Bones != null) weighted++; }
            else if (s.Attachment is RegionAttachment) regions++;
        }
        Console.WriteLine($"OK bones={bonesOut.Count} regions={regions} meshes={meshes} (weighted={weighted}) pages={pages.Count}");
    }

    static List<object> ParseWeights(MeshAttachment ma, int fallbackBone)
    {
        var res = new List<object>();
        int verts = ma.WorldVerticesLength / 2;
        if (ma.Bones == null)
        {
            for (int i = 0; i < verts; i++)
                res.Add(new object[] { new object[] { fallbackBone, 1f } });
            return res;
        }
        int[] bs = ma.Bones;
        float[] vs = ma.Vertices;
        int vb = 0, vv = 0;
        for (int i = 0; i < verts; i++)
        {
            int cnt = bs[vb++];
            var list = new List<object>();
            for (int k = 0; k < cnt; k++)
            {
                int bi = bs[vb++];
                float x = vs[vv++], y = vs[vv++], w = vs[vv++];
                list.Add(new object[] { bi, w });
            }
            res.Add(list);
        }
        return res;
    }
}
