using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenDiablo2.Common.Models
{

    public class ImageFrame : IDisposable
    {
        public UInt32 Flip;
        public UInt32 Width;
        public UInt32 Height;
        public Int32 OffsetX;
        public Int32 OffsetY;        // from bottom border, not up 
        public UInt32 Unknown;
        public UInt32 NextBlock;
        public UInt32 Length;
        public Int16[] ImageData;

        public void Dispose()
        {
            ImageData = new Int16[0];
        }

        public UInt32 GetColor(int x, int y, Palette palette)
        {
            var index = ImageData[x + (y * Width)];
            if (index == -1)
                return 0;

            var color = palette.Colors[index];

            return ((UInt32)255 << 24) + ((UInt32)color.R << 16) + ((UInt32)color.G << 8) + (UInt32)color.B;
        }
    }

    public sealed class ImageSet : IDisposable
    {
        static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private UInt32 version;
        private UInt32 unknown1;           // 01 00 00 00  ???
        private UInt32 unknown2;           // 00 00 00 00  ???
        private UInt32 termination;        // EE EE EE EE or CD CD CD CD  ???
        private UInt32[] framePointers;
        public ImageFrame[] Frames { get; private set; }

        public UInt32 Directions { get; private set; }
        public UInt32 FramesPerDirection { get; private set; }

        public static ImageSet LoadFromStream(Stream stream)
        {
            var br = new BinaryReader(stream);
            var result = new ImageSet
            {
                version = br.ReadUInt32(),
                unknown1 = br.ReadUInt32(),
                unknown2 = br.ReadUInt32(),
                termination = br.ReadUInt32(),
                Directions = br.ReadUInt32(),
                FramesPerDirection = br.ReadUInt32()
            };

            result.framePointers = new uint[result.Directions * result.FramesPerDirection];
            for (var i = 0; i < result.Directions * result.FramesPerDirection; i++)
                result.framePointers[i] = br.ReadUInt32();

            result.Frames = new ImageFrame[result.Directions * result.FramesPerDirection];
            for (var i = 0; i < result.Directions * result.FramesPerDirection; i++)
            {
                stream.Seek(result.framePointers[i], SeekOrigin.Begin);

                result.Frames[i] = new ImageFrame
                {
                    Flip = br.ReadUInt32(),
                    Width = br.ReadUInt32(),
                    Height = br.ReadUInt32(),
                    OffsetX = br.ReadInt32(),
                    OffsetY = br.ReadInt32(),
                    Unknown = br.ReadUInt32(),
                    NextBlock = br.ReadUInt32(),
                    Length = br.ReadUInt32()
                };

                result.Frames[i].ImageData = new Int16[result.Frames[i].Width * result.Frames[i].Height];
                for (int ty = 0; ty < result.Frames[i].Height; ty++)
                    for (int tx = 0; tx < result.Frames[i].Width; tx++)
                        result.Frames[i].ImageData[tx + (ty * result.Frames[i].Width)] = -1;


                int x = 0;
                int y = (int)result.Frames[i].Height - 1;
                while (true)
                {
                    var b = br.ReadByte();
                    if (b == 0x80)
                    {
                        if (y == 0)
                            break;

                        y--;
                        x = 0;
                        continue;
                    }

                    if ((b & 0x80) > 0)
                    {
                        var transparentPixelsToWrite = b & 0x7F;

                        Enumerable.Repeat<Int16>(-1, transparentPixelsToWrite).ToArray()
                            .CopyTo(result.Frames[i].ImageData, x + (y * result.Frames[i].Width));
                        x += transparentPixelsToWrite;

                        continue;
                    }


                    br.ReadBytes(b).CopyTo(result.Frames[i].ImageData, x + (y * result.Frames[i].Width));
                    x += b;



                }
            }
            return result;
        }

        public void Dispose()
        {
        }
    }
}
