#nullable disable

using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.Rar;
using SharpCompress.IO;

namespace SharpCompress.Compressors.PPMd.H;

internal class RangeCoder
{
    internal const int TOP = 1 << 24;
    internal const int BOT = 1 << 15;
    internal const long UINT_MASK = 0xFFFFffffL;

    // uint low, code, range;
    private long _low,
        _code,
        _range;
    private IRarUnpack _unpackRead;
    private readonly Stream _stream;

    internal RangeCoder(IRarUnpack unpackRead)
    {
        _unpackRead = unpackRead;
        Init();
    }

    internal RangeCoder(Stream stream)
    {
        _stream = stream;
        Init();
    }

    internal RangeCoder() { }

    internal async ValueTask InitAsync(
        IRarUnpack unpackRead,
        CancellationToken cancellationToken = default
    )
    {
        _unpackRead = unpackRead;
        SubRange = new SubRange();

        _low = _code = 0L;
        _range = 0xFFFFffffL;
        for (var i = 0; i < 4; i++)
        {
            _code =
                ((_code << 8) | await ReadCharAsync(cancellationToken).ConfigureAwait(false))
                & UINT_MASK;
        }
    }

    private void Init()
    {
        SubRange = new SubRange();

        _low = _code = 0L;
        _range = 0xFFFFffffL;
        for (var i = 0; i < 4; i++)
        {
            _code = ((_code << 8) | ReadChar()) & UINT_MASK;
        }
    }

    internal async ValueTask InitAsync(Stream stream, CancellationToken cancellationToken = default)
    {
        SubRange = new SubRange();

        _low = _code = 0L;
        _range = 0xFFFFffffL;

        byte[] buffer = new byte[4];
        await stream.ReadFullyAsync(buffer, 0, 4, cancellationToken).ConfigureAwait(false);

        for (var i = 0; i < 4; i++)
        {
            _code = ((_code << 8) | buffer[i]) & UINT_MASK;
        }
    }

    internal int CurrentCount
    {
        get
        {
            _range = (_range / SubRange.Scale) & UINT_MASK;
            return (int)((_code - _low) / (_range));
        }
    }

    private long ReadChar()
    {
        if (_unpackRead != null)
        {
            return (_unpackRead.ReadChar());
        }
        if (_stream != null)
        {
            return _stream.ReadByte();
        }
        return -1;
    }

    internal SubRange SubRange { get; private set; }

    internal long GetCurrentShiftCount(int shift)
    {
        _range = Utility.URShift(_range, shift);
        return ((_code - _low) / (_range)) & UINT_MASK;
    }

    internal void Decode()
    {
        _low = (_low + (_range * SubRange.LowCount)) & UINT_MASK;
        _range = (_range * (SubRange.HighCount - SubRange.LowCount)) & UINT_MASK;
    }

    internal void AriDecNormalize()
    {
        //		while ((low ^ (low + range)) < TOP || range < BOT && ((range = -low & (BOT - 1)) != 0 ? true : true))
        //		{
        //			code = ((code << 8) | unpackRead.getChar()&0xff)&uintMask;
        //			range = (range << 8)&uintMask;
        //			low = (low << 8)&uintMask;
        //		}

        // Rewrote for clarity
        var c2 = false;
        while ((_low ^ (_low + _range)) < TOP || (c2 = _range < BOT))
        {
            if (c2)
            {
                _range = (-_low & (BOT - 1)) & UINT_MASK;
                c2 = false;
            }
            _code = ((_code << 8) | ReadChar()) & UINT_MASK;
            _range = (_range << 8) & UINT_MASK;
            _low = (_low << 8) & UINT_MASK;
        }
    }

    private async ValueTask<long> ReadCharAsync(CancellationToken cancellationToken = default)
    {
        if (_unpackRead != null)
        {
            return await _unpackRead.ReadCharAsync(cancellationToken).ConfigureAwait(false);
        }
        if (_stream != null)
        {
            byte[] buffer = new byte[1];
            await _stream.ReadFullyAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false);
            return buffer[0];
        }
        return -1;
    }

    internal async ValueTask AriDecNormalizeAsync(CancellationToken cancellationToken = default)
    {
        var c2 = false;
        while ((_low ^ (_low + _range)) < TOP || (c2 = _range < BOT))
        {
            if (c2)
            {
                _range = (-_low & (BOT - 1)) & UINT_MASK;
                c2 = false;
            }
            _code =
                ((_code << 8) | await ReadCharAsync(cancellationToken).ConfigureAwait(false))
                & UINT_MASK;
            _range = (_range << 8) & UINT_MASK;
            _low = (_low << 8) & UINT_MASK;
        }
    }

    // Debug
    public override string ToString()
    {
        var buffer = new StringBuilder();
        buffer.Append("RangeCoder[");
        buffer.Append("\n  low=");
        buffer.Append(_low);
        buffer.Append("\n  code=");
        buffer.Append(_code);
        buffer.Append("\n  range=");
        buffer.Append(_range);
        buffer.Append("\n  subrange=");
        buffer.Append(SubRange);
        buffer.Append(']');
        return buffer.ToString();
    }
}

internal class SubRange
{
    // uint LowCount, HighCount, scale;
    private long _lowCount,
        _highCount,
        _scale;

    internal void IncScale(int dScale) => Scale += dScale;

    internal long HighCount
    {
        get => _highCount;
        set => _highCount = value & RangeCoder.UINT_MASK;
    }

    internal long LowCount
    {
        get => _lowCount & RangeCoder.UINT_MASK;
        set => _lowCount = value & RangeCoder.UINT_MASK;
    }

    internal long Scale
    {
        get => _scale;
        set => _scale = value & RangeCoder.UINT_MASK;
    }

    // Debug
    public override string ToString()
    {
        var buffer = new StringBuilder();
        buffer.Append("SubRange[");
        buffer.Append("\n  lowCount=");
        buffer.Append(_lowCount);
        buffer.Append("\n  highCount=");
        buffer.Append(_highCount);
        buffer.Append("\n  scale=");
        buffer.Append(_scale);
        buffer.Append(']');
        return buffer.ToString();
    }
}
