using LibDgf.Txm;
using System;
using System.Collections.Generic;
using System.Text;

namespace LibDgf.Ps2.Gs
{
    public static class PsmtMixer
    {
        static int[,] PSMCT32_TO_PSMT4_COL02_LOOKUP = new int[16, 8];
        static int[,] PSMCT32_TO_PSMT4_COL13_LOOKUP = new int[16, 8];
        static int[,] PSMCT32_TO_PSMT8_COL02_LOOKUP = new int[16, 4];
        static int[,] PSMCT32_TO_PSMT8_COL13_LOOKUP = new int[16, 4];

        static PsmtMixer()
        {
            FillLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, false);
            FillOddLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, PSMCT32_TO_PSMT4_COL13_LOOKUP);
            FillLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, false);
            FillOddLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, PSMCT32_TO_PSMT8_COL13_LOOKUP);

            //PrintLookup(PSMCT32_TO_PSMT4_COL02_LOOKUP, nameof(PSMCT32_TO_PSMT4_COL02_LOOKUP));
            //PrintLookup(PSMCT32_TO_PSMT4_COL13_LOOKUP, nameof(PSMCT32_TO_PSMT4_COL13_LOOKUP));
            //PrintLookup(PSMCT32_TO_PSMT8_COL02_LOOKUP, nameof(PSMCT32_TO_PSMT8_COL02_LOOKUP));
            //PrintLookup(PSMCT32_TO_PSMT8_COL13_LOOKUP, nameof(PSMCT32_TO_PSMT8_COL13_LOOKUP));
        }

        static void PrintLookup(int[,] lookup, string name)
        {
            Console.WriteLine(name);
            int rowNum = lookup.GetLength(0);
            int colNum = lookup.GetLength(1);
            for (int row = 0; row < rowNum; ++row)
            {
                for (int col = colNum - 1; col >= 0; --col)
                {
                    Console.Write("{0,3} ", lookup[row, col]);
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

        static void FillLookup(int[,] lookup, bool skipNonConsec)
        {
            int numCols = lookup.GetLength(1);
            int num = 0;

            // Phase 1: consecutive numbers
            // Fill every second column
            // Top half then bottom half
            for (int half = 0; half < 2; ++half)
            {
                for (int col = 0; col < numCols; col += skipNonConsec ? 1 : 2)
                {
                    for (int row = 0; row < 8; ++row)
                    {
                        lookup[half * 8 + row, col] = num++;
                    }
                }
            }

            // Phase 2: wrapped numbers
            if (!skipNonConsec)
            {
                for (int half = 0; half < 2; ++half)
                {
                    for (int col = 1; col < numCols; col += 2)
                    {
                        for (int row = 4; row < 12; ++row)
                        {
                            lookup[half * 8 + (row % 8), col] = num++;
                        }
                    }
                }
            }
        }

        static void FillOddLookup(int[,] evenLookup, int[,] oddLookup)
        {
            int numCols = evenLookup.GetLength(1);
            for (int half = 0; half < 2; ++half)
            {
                for (int i = 0; i < 8; ++i)
                {
                    for (int j = 0; j < numCols; ++j)
                    {
                        oddLookup[half * 8 + i, j] = evenLookup[half * 8 + ((i + 4) % 8), j];
                    }
                }
            }
        }

        public static byte[] MixColumn(byte[] column, TxmPixelFormat srcFormat, TxmPixelFormat destFormat, bool isOdd)
        {
            if (srcFormat != TxmPixelFormat.PSMCT32)
                throw new NotSupportedException("Only PSMCT32 supported as source format.");
            switch (destFormat)
            {
                case TxmPixelFormat.PSMT4:
                    return MixColumn32To4(column, isOdd);
                case TxmPixelFormat.PSMT8:
                    return MixColumn32To8(column, isOdd);
                default:
                    throw new NotSupportedException($"{destFormat} not supported as destination format.");
            }
        }

        static byte[] MixColumn32To4(byte[] column, bool isOdd)
        {
            int[,] lookup = isOdd ? PSMCT32_TO_PSMT4_COL13_LOOKUP : PSMCT32_TO_PSMT4_COL02_LOOKUP;
            int numCol = lookup.GetLength(1);
            byte[] dest = new byte[column.Length];
            byte b = 0;
            for (int i = 0; i < column.Length * 2; ++i)
            {
                if (i % 2 == 0)
                    b = column[i / 2];
                else
                    b >>= 4;

                int index = lookup[i / numCol, i % numCol];
                dest[index / 2] |= (byte)((b & 0x0f) << (index % 2 * 4));
            }
            return dest;
        }

        static byte[] MixColumn32To8(byte[] column, bool isOdd)
        {
            int[,] lookup = isOdd ? PSMCT32_TO_PSMT8_COL13_LOOKUP : PSMCT32_TO_PSMT8_COL02_LOOKUP;
            int numCol = lookup.GetLength(1);
            byte[] dest = new byte[column.Length];
            for (int i = 0; i < column.Length; ++i)
            {
                int index = lookup[i / numCol, i % numCol];
                dest[index] = column[i];
            }
            return dest;
        }
    }
}