// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.Runtime.InteropServices;
using System.Text;

namespace System.Windows.Forms.Metafiles;

/// <summary>
///  Record that represents a 16 bit Poly record.
/// </summary>
/// <devdoc>
///  Not an actual Win32 define, encapsulates:
///
///   - EMRPOLYPOLYLINE16
///   - EMRPOLYPOLYBEZIER16
///   - EMRPOLYPOLYGON16
///   - EMRPOLYPOLYLINETO16
/// </devdoc>
[StructLayout(LayoutKind.Sequential)]
internal struct EMRPOLYPOLY16
{
    public EMR emr;
    public RECT rclBounds;          // Inclusive-inclusive bounds in device units
    public uint nPolys;             // Number of polys
    public uint cpts;               // Total number of points in all polys
    public uint _aPolyCounts;       // Array of point counts for each poly

    // Can't represent this as a field as it comes nPolys uints after cpts
    // public POINTS apts[1];        // Array of points

    public override string ToString() => ToString(null);

    public string ToString(DeviceContextState? state)
    {
        StringBuilder sb = new(512);
        sb.Append($"[EMR{emr.iType}] Bounds: {rclBounds} Poly count: {nPolys} Total points: {cpts}");

        for (int i = 0; i < nPolys; i++)
        {
            if (state is null)
            {
                sb.Append($"\n\tPoly index {i}: {string.Join(' ', GetPointsForPoly(i).ToArray())}");
            }
            else
            {
                sb.Append($"\n\tPoly index {i}: {string.Join(' ', GetPointsForPoly(i).Transform(p => state.TransformPoint(p)))}");
            }
        }

        return sb.ToString();
    }

    public unsafe ReadOnlySpan<uint> aPolyCounts
    {
        get
        {
            fixed (uint* c = &_aPolyCounts)
            {
                return new(c, checked((int)nPolys));
            }
        }
    }

    public unsafe ReadOnlySpan<POINTS> GetPointsForPoly(int index)
    {
        if (index < 0 || index >= nPolys)
            throw new ArgumentOutOfRangeException(nameof(index));

        int current = 0;
        fixed (void* s = &emr)
        {
            POINTS* currentPoint = (POINTS*)((byte*)s + sizeof(EMRPOLYPOLY16) + (sizeof(uint) * (nPolys - 1)));
            var counts = aPolyCounts;
            while (current != index)
            {
                currentPoint += counts[current];
                current++;
            }

            return new ReadOnlySpan<POINTS>(currentPoint, (int)counts[current]);
        }
    }
}
