using System;
using System.Diagnostics;

namespace Ryujinx.Graphics.Texture
{
    class ASTCPixel
    {
        public short R { get; set; }
        public short G { get; set; }
        public short B { get; set; }
        public short A { get; set; }

        byte[] BitDepth = new byte[4];

        public ASTCPixel(short _A, short _R, short _G, short _B)
        {
            A = _A;
            R = _R;
            G = _G;
            B = _B;

            for (int i = 0; i < 4; i++)
                BitDepth[i] = 8;
        }

        public void ClampByte()
        {
            R = Math.Min(Math.Max(R, (short)0), (short)255);
            G = Math.Min(Math.Max(G, (short)0), (short)255);
            B = Math.Min(Math.Max(B, (short)0), (short)255);
            A = Math.Min(Math.Max(A, (short)0), (short)255);
        }

        public short GetComponent(int Index)
        {
            switch(Index)
            {
                case 0: return A;
                case 1: return R;
                case 2: return G;
                case 3: return B;
            }

            return 0;
        }

        public void SetComponent(int Index, int Value)
        {
            switch (Index)
            {
                case 0:
                    A = (short)Value;
                    break;
                case 1:
                    R = (short)Value;
                    break;
                case 2:
                    G = (short)Value;
                    break;
                case 3:
                    B = (short)Value;
                    break;
            }
        }

        public void ChangeBitDepth(byte[] Depth)
        {
            for(int i = 0; i< 4; i++)
            {
                int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);

                SetComponent(i, Value);
                BitDepth[i] = Depth[i];
            }
        }

        short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
        {
            Debug.Assert(NewDepth <= 8);
            Debug.Assert(OldDepth <= 8);

            if (OldDepth == NewDepth)
            {
                // Do nothing
                return Value;
            }
            else if (OldDepth == 0 && NewDepth != 0)
            {
                return (short)((1 << NewDepth) - 1);
            }
            else if (NewDepth > OldDepth)
            {
                return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
            }
            else
            {
                // oldDepth > newDepth
                if (NewDepth == 0)
                {
                    return 0xFF;
                }
                else
                {
                    byte BitsWasted = (byte)(OldDepth - NewDepth);
                    short TempValue = Value;

                    TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
                    TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));

                    return (byte)(TempValue);
                }
            }
        }

        public int Pack()
        {
            ASTCPixel NewPixel   = new ASTCPixel(A, R, G, B);
            byte[] eightBitDepth = { 8, 8, 8, 8 };

            NewPixel.ChangeBitDepth(eightBitDepth);

            return (byte)NewPixel.A << 24 |
                   (byte)NewPixel.B << 16 |
                   (byte)NewPixel.G << 8  |
                   (byte)NewPixel.R << 0;
        }

        // Adds more precision to the blue channel as described
        // in C.2.14
        public static ASTCPixel BlueContract(int a, int r, int g, int b)
        {
            return new ASTCPixel((short)(a),
                                 (short)((r + b) >> 1),
                                 (short)((g + b) >> 1),
                                 (short)(b));
        }
    }
}
