using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using SoulstormReplayReader.Core.Domain.Player;
using SoulstormReplayReader.Core.Models;

namespace SoulstormReplayReader.Core.Extensions;

public static class Extensions
{
    public static void RequireInRange(this int num, int min, int max, string errorMsg)
    {
        if (num < min || num > max)
            throw new ArgumentOutOfRangeException(errorMsg);
    }

    public static void Swap<T>(this IList<T> list, int i1, int i2)
    {
        (list[i1], list[i2]) = (list[i2], list[i1]);
    }

    public static Span<byte> TrimEnd(this Span<byte> span, byte val = 0)
    {
        var i = span.Length - 1;
        while (i > 0 && span[i--] == val) ;
        return span[..i];
    }

    public static ExBinaryReader NextAsciiStringMustEqual(this ExBinaryReader binReader, string text)
    {
        if (!binReader.IsNextAsciiStringEquals(text))
        {
            throw new Exception(text + "Нарушение структуры файла. ");
        }

        return binReader;
    }

    public static bool IsNextAsciiStringEquals(this ExBinaryReader binReader, string text)
    {
        Span<byte> textSpan = Encoding.ASCII.GetBytes(text);
        var nextTextSpan = binReader.ReadBytes(stackalloc byte[text.Length]);

        return textSpan.Length == nextTextSpan.Length && SpansEqualInvariant(textSpan, nextTextSpan);

        static bool SpansEqualInvariant(ReadOnlySpan<byte> span1, ReadOnlySpan<byte> span2)
        {
            for (var i = 0; i < span1.Length; i++)
            {
                if (span1[i] == span2[i])
                    continue;

                var char1 = char.ToLower((char)span1[i]);
                var char2 = char.ToLower((char)span2[i]);
                if (char1 != char2)
                {
                    return false;
                }
            }

            return true;
        }
    }

    public static ReplayColor ReadBgraToArgb(this ExBinaryReader binReader)
    {
        var bgra = binReader.ReadUInt32();

        var argb = ReverseBytes(bgra);

        return Unsafe.As<uint, ReplayColor>(ref argb);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static uint ReverseBytes(uint value) =>
        ((value & 0x000000FFU) << 24) | ((value & 0x0000FF00U) << 8) |
        ((value & 0x00FF0000U) >> 8) | ((value & 0xFF000000U) >> 24);

    public static string ToContentString(this IEnumerable<byte> bytes) =>
        string.Join(' ',
            bytes.Select(b => $"{b:x}".PadLeft(2, '0'))
        );

    [Conditional("DEBUGLOGGING")]
    public static void LogPlayers(this List<PlayerModel> players)
    {
        Console.WriteLine("NAME            TEAM     TYPE         RACE");
        foreach (var pl in players.Where(p => p != null))
        {
            Console.WriteLine($"{pl.Name ?? "",-15} {pl.Team,-8} {pl.RawType} {pl.ResolvedType,-10} {pl.Race}");
        }
    }
}