namespace Gma.QrCodeNet.Encoding.Terminate;

internal static class Terminator
{
    private const int NumBitsForByte = 8;

    /// <summary>
    /// This method will create BitList that contains
    /// terminator, padding and pad codewords for given datacodewords.
    /// Use it to full fill the data codewords capacity. Thus avoid massive empty bits.
    /// </summary>
    /// <remarks>ISO/IEC 18004:2006 P. 32 33.
    /// Terminator / Bit stream to codeword conversion</remarks>
    /// <param name="baseList">Method will add terminator bits (Terminator, padding and padcodewords) at end of baseList</param>
    /// <param name="dataCount">Num of bits for datacodewords without terminator</param>
    /// <param name="numTotalDataCodewords">Total number of datacodewords for specific version.
    /// Receive it under Version/VersionTable</param>
    internal static void TerminateBites(this BitList baseList, int dataCount, int numTotalDataCodewords)
    {
        int numTotalDataBits = numTotalDataCodewords << 3;
        int numDataBits = dataCount;

        int numFillerBits = numTotalDataBits - numDataBits;
        int numBitsNeedForLastByte = numFillerBits & 0x7;
        int numFillerBytes = numFillerBits >> 3;

        // BitList result = new BitList();
        if (numBitsNeedForLastByte >= QRCodeConstantVariable.TerminatorLength)
        {
            baseList.TerminatorPadding(numBitsNeedForLastByte);
            baseList.PadeCodewords(numFillerBytes);
        }
        else if (numFillerBytes == 0)
        {
            baseList.TerminatorPadding(numBitsNeedForLastByte);
        }
        else if (numFillerBytes > 0)
        {
            baseList.TerminatorPadding(numBitsNeedForLastByte + NumBitsForByte);
            baseList.PadeCodewords(numFillerBytes - 1);
        }

        if (baseList.Count != numTotalDataBits)
        {
            throw new ArgumentException(
                $"Generate terminator and Padding fail. Num of bits need: {numFillerBytes}. Actual length: {baseList.Count - numDataBits}");
        }
    }

    private static void PadeCodewords(this BitList mainList, int numOfPadeCodewords)
    {
        if (numOfPadeCodewords < 0)
        {
            throw new ArgumentException("Num of pade codewords is less than Zero");
        }

        for (int numOfP = 1; numOfP <= numOfPadeCodewords; numOfP++)
        {
            if (numOfP % 2 == 1)
            {
                mainList.Add(QRCodeConstantVariable.PadeCodewordsOdd, NumBitsForByte);
            }
            else
            {
                mainList.Add(QRCodeConstantVariable.PadeCodewordsEven, NumBitsForByte);
            }
        }
    }

    private static void TerminatorPadding(this BitList mainList, int numBits) => mainList.Add(QRCodeConstantVariable.TerminatorNPaddingBit, numBits);
}