﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using System.IO;
using FSO.Files.Utils;

namespace FSO.Files.Formats.IFF.Chunks
{
	public static class SPR2FrameEncoder
    {

        public delegate Color[] QuantizerFunction(SPR2Frame frame, out byte[] bytes);

        public static QuantizerFunction QuantizeFrame;

        public static void WriteFrame(SPR2Frame frame, IoWriter output)
        {
            var bytes = frame.PalData;
            var col = frame.PixelData;
            var zs = frame.ZBufferData;

            int index = 0;
            int blankLines = 0;
            for (int y=0; y<frame.Height; y++)
            {
                byte lastCmd = 0;
                List<byte> dataBuf = new List<byte>();
                int rlecount = 0;
                bool anySolid = false;

                var scanStream = new MemoryStream();
                var scanOut = IoWriter.FromStream(scanStream, ByteOrder.LITTLE_ENDIAN);

                for (int x=0; x<frame.Width; x++)
                {
                    byte plt = bytes[index];
                    byte a = col[index].A;
                    byte z = zs[index];

                    var cmd = getCmd(plt, a, z);

                    if (x == 0 || cmd != lastCmd)
                    {
                        if (x != 0)
                        {
                            //write a command to write the last sequence of pixels
                            scanOut.WriteUInt16((ushort)(((int)lastCmd<<13)|rlecount));
                            if ((dataBuf.Count % 2) != 0) dataBuf.Add(0);
                            scanOut.WriteBytes(dataBuf.ToArray());
                            dataBuf.Clear();
                        }

                        lastCmd = cmd;
                        rlecount = 0;
                    }

                    switch (cmd)
                    {
                        case 0x1:
                        case 0x2:
                            dataBuf.Add(z);
                            dataBuf.Add(plt);
                            if (cmd == 0x2) dataBuf.Add((byte)Math.Ceiling(a/ 8.2258064516129032258064516129032));
                            anySolid = true;
                            break;
                        case 0x6:
                            dataBuf.Add(plt);
                            anySolid = true;
                            break;
                        default:
                            break;
                    }
                    rlecount++;

                    index++;
                }

                if (anySolid)
                {
                    //write a command to write the last sequence of pixels
                    scanOut.WriteUInt16((ushort)(((int)lastCmd << 13) | rlecount));
                    if ((dataBuf.Count % 2) != 0) dataBuf.Add(0);
                    scanOut.WriteBytes(dataBuf.ToArray());
                    dataBuf.Clear();
                }

                var scanData = scanStream.ToArray();

                if (scanData.Length == 0)
                    blankLines++; //line is transparent
                else 
                {
                    if (blankLines > 0)
                    {
                        //add transparent lines before our new command
                        output.WriteUInt16((ushort)((0x4<<13) | blankLines));
                        blankLines = 0;
                    }
                    output.WriteUInt16((ushort)(scanData.Length+2));
                    output.WriteBytes(scanData);
                }

            }

            if (blankLines > 0)
            {
                //add transparent lines before our new command
                output.WriteUInt16((ushort)((0x4 << 13) | blankLines));
                blankLines = 0;
            }
            output.WriteUInt16((ushort)(0x5<<13));

            return;
        }

        private static byte getCmd(byte col, byte a, byte z)
        {
            if (a == 0) return 0x03; //transparent fill
            else if (a < 255) return 0x02; // col,a,z
            else if (z > 0) return 0x01; // col,z
            else return 0x06; // col
        }
    }
}
