using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows;

namespace DSPRE.Editors.Utils
{
    public class IndexedBitmapHandler
    {
        public IndexedBitmapHandler()
        {
        }

        public Bitmap Convert(Bitmap source, PixelFormat target)
        {
            if ((source.PixelFormat == target) || (target == PixelFormat.DontCare))
                return source;
            Bitmap image = new Bitmap(source.Width, source.Height, target);
            if ((source.PixelFormat == PixelFormat.Format4bppIndexed) && (target == PixelFormat.Format8bppIndexed))
            {
                byte[] array = GetArray(source);
                byte[] array2 = new byte[(array.Length * 2)];
                byte current;
                for (int i = 0; i < array.Length; i++)
                {
                    current = array[i];
                    array2[2 * i] = (byte)((current & 0xF0) >> 4);
                    array2[2 * i + 1] = (byte)((current & 0x0F));
                }
                image = MakeImage(source.Width, source.Height, array2, target);
                image.Palette = source.Palette;
            }
            if (((source.PixelFormat == PixelFormat.Format24bppRgb) || (source.PixelFormat == PixelFormat.Format32bppArgb)) && (target == PixelFormat.Format8bppIndexed))
            {
                int skip = 3;
                if (source.PixelFormat == PixelFormat.Format32bppArgb)
                    skip = 4;
                Color[] newPalette = new Color[256];
                byte[] array = new byte[source.Width * source.Height];
                byte[] sourceArray = GetArray(source);
                Color Pixel = Color.FromArgb(sourceArray[2], sourceArray[1], sourceArray[0]);
                newPalette[0] = Pixel;
                int index = 1;
                array[0] = 0;
                byte r = 0;
                byte g = 0;
                byte b = 0;
                bool match = false;
                for (int i = 1; i < (source.Width * source.Height); i++)
                {
                    b = sourceArray[(i * skip)];
                    g = sourceArray[(i * skip + 1)];
                    r = sourceArray[(i * skip + 2)];
                    Pixel = Color.FromArgb(r, g, b);
                    for (int j = 0; j < index; j++)
                    {
                        if (Pixel == newPalette[j])
                        {
                            array[i] = (byte)j;
                            match = true;
                            break;
                        }
                    }
                    if (!match)
                    {
                        if (index >= 256)
                        {
                            MessageBox.Show("Conversion failed");
                            return null;
                        }
                        newPalette[index] = Pixel;
                        array[i] = (byte)index;
                        index++;
                    }
                    match = false;
                }
                image = MakeImage(source.Width, source.Height, array, target);
                ColorPalette oldPalette = image.Palette;
                for (int i = 0; i < index; i++)
                {
                    oldPalette.Entries[i] = newPalette[i];
                }
                image.Palette = oldPalette;
            }
            return image;
        }

        public Bitmap Resize(Bitmap source, int top, int bottom, int left, int right)
        {
            int newWidth = source.Width + left + right;
            int newHeight = source.Height + top + bottom;
            int arraysize = newWidth * newHeight;
            ColorPalette palette = source.Palette;

            byte[] source_array = GetArray(source);

            byte[] byte_array = new byte[arraysize];
            byte background = source_array[0];

            int source_index = 0;
            int index = 0;
            for (int j = 0; j < newHeight; j++)
            {
                for (int i = 0; i < newWidth; i++)
                {
                    if (i < left || i >= (left + source.Width) || j < top || j >= (top + source.Height))
                        byte_array[index] = background;
                    else
                    {
                        source_index = ((j - top) * source.Width) + i - left;
                        byte_array[index] = source_array[source_index];
                    }
                    index++;
                }
            }
            Bitmap newImage = MakeImage(newWidth, newHeight, byte_array, PixelFormat.Format8bppIndexed);
            newImage.Palette = palette;
            return newImage;
        }

        public Bitmap Concat(Bitmap first, Bitmap second)
        {
            int newWidth = first.Width + second.Width;
            int newHeight = first.Height;
            if (first.Height < second.Height)
                newHeight = second.Height;
            int arraysize = newWidth * newHeight;
            ColorPalette palette = first.Palette;

            byte[] first_array = GetArray(first);
            byte[] second_array = GetArray(second);

            byte[] byte_array = new byte[arraysize];
            byte background = first_array[0];

            int source_index = 0;
            int index = 0;
            for (int j = 0; j < newHeight; j++)
            {
                for (int i = 0; i < first.Width; i++)
                {
                    if (j > first.Height)
                        byte_array[index] = background;
                    else
                    {
                        source_index = ((j * first.Width) + i);
                        byte_array[index] = first_array[source_index];
                    }
                    index++;
                }
                for (int i = 0; i < second.Width; i++)
                {
                    if (j > second.Height)
                        byte_array[index] = background;
                    else
                    {
                        source_index = ((j * second.Width) + i);
                        byte_array[index] = second_array[source_index];
                    }
                    index++;
                }
            }
            Bitmap newImage = MakeImage(newWidth, newHeight, byte_array, PixelFormat.Format8bppIndexed);
            newImage.Palette = palette;
            return newImage;
        }

        public Bitmap[] Split(Bitmap source, int tilewidth, int tileheight)
        {
            int maxTiles = (source.Width / tilewidth) * (source.Height / tileheight);
            Bitmap[] tiles = new Bitmap[maxTiles];
            int index = 0;
            int x_index = 0;
            int y_index = 0;
            while (y_index + tileheight <= source.Height)
            {
                if (x_index + tilewidth <= source.Width)
                {
                    tiles[index] = Resize(source, (y_index * -1), ((source.Height - y_index - tileheight) * -1), (x_index * -1), ((source.Width - x_index - tilewidth) * -1));
                    index++;
                }
                x_index += tilewidth;
                if (x_index + tilewidth > source.Width)
                {
                    y_index += tileheight;
                    x_index = 0;
                }
            }
            return tiles;
        }

        public Bitmap ShrinkImage(Bitmap image, int x_scale, int y_scale)
        {
            byte[] array = GetArray(image);
            ColorPalette palette = image.Palette;
            int new_width = image.Width / x_scale;
            int new_height = image.Height / y_scale;
            byte[] array2 = new byte[new_width * new_height];
            for (int j = 0; j < new_height; j++)
            {
                for (int i = 0; i < new_width; i++)
                {
                    array2[j * new_width + i] = array[y_scale * j * image.Width + i * x_scale];
                }
            }
            Bitmap temp = MakeImage(new_width, new_height, array2, image.PixelFormat);
            temp.Palette = palette;
            return temp;
        }

        public ColorPalette AlternatePalette(Bitmap parent, Bitmap child)
        {
            Bitmap temp = new Bitmap(1, 1, parent.PixelFormat);
            ColorPalette newPalette = temp.Palette;
            ColorPalette ChildPalette = child.Palette;
            byte[] ParentArray = GetArray(parent);
            byte[] ChildArray = GetArray(child);
            if (ParentArray.Length != ChildArray.Length)
                return null;
            for (int i = 0; i < ChildPalette.Entries.Length; i++)
            {
                for (int j = 0; j < ParentArray.Length; j++)
                {
                    if (ParentArray[j] == i)
                    {
                        newPalette.Entries[i] = ChildPalette.Entries[ChildArray[j]];
                        break;
                    }
                }
            }
            return newPalette;
        }

        public Bitmap PaletteMatch(ColorPalette parent, Bitmap child, bool[] used = null)
        {
            if (parent.Entries == child.Palette.Entries)
                return child;
            if (used == null)
            {
                used = new bool[parent.Entries.Length];
                for (int i = 0; i < parent.Entries.Length; i++)
                    used[i] = true;
            }
            Bitmap image = ShrinkPalette(child);
            Bitmap temp = new Bitmap(1, 1, child.PixelFormat);
            ColorPalette childPalette = image.Palette;
            ColorPalette newPalette = temp.Palette;
            int size = PaletteSize(image);
            byte[] array = GetArray(image);
            byte[] indexof = new byte[size];
            bool[] NotFound = new bool[size];
            for (int i = 0; i < size; i++)
                NotFound[i] = true;
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < parent.Entries.Length; j++)
                {
                    if (parent.Entries[j] == childPalette.Entries[i])
                    {
                        indexof[i] = (byte)j;
                        NotFound[i] = false;
                        break;
                    }
                }
            }
            int maxsize = 0;
            for (int i = 0; i < parent.Entries.Length; i++)
            {
                if (used[i])
                {
                    newPalette.Entries[i] = parent.Entries[i];
                    maxsize++;
                }
            }
            for (int i = 0; i < size; i++)
            {
                if (NotFound[i])
                    maxsize++;
            }
            for (int i = 0; i < maxsize; i++)
            {
                if ((i < used.Length) && (used[i]))
                    continue;
                for (int j = 0; j < size; j++)
                {
                    if (NotFound[j])
                    {
                        indexof[j] = (byte)i;
                        newPalette.Entries[i] = childPalette.Entries[j];
                        NotFound[j] = false;
                        break;
                    }
                }
            }
            byte[] newArray = new byte[array.Length];
            for (int i = 0; i < array.Length; i++)
            {
                newArray[i] = indexof[array[i]];
            }
            image = MakeImage(child.Width, child.Height, newArray, child.PixelFormat);
            image.Palette = newPalette;
            return image;
        }

        public Bitmap ShrinkPalette(Bitmap image, bool[] used = null)
        {
            used = IsUsed(image, used);
            byte[] array = GetArray(image);
            ColorPalette oldPalette = image.Palette;
            Bitmap temp = new Bitmap(image.Width, image.Height, image.PixelFormat);
            ColorPalette newPalette = temp.Palette;
            int size = oldPalette.Entries.Length;
            int index = 0;
            int unused = 0;
            byte[] indexof = new byte[size];
            for (int i = 0; i < size; i++)
            {
                if (used[i])
                {
                    newPalette.Entries[index] = oldPalette.Entries[i];
                    indexof[i] = (byte)index;
                    index++;
                }
                else
                    unused++;
            }
            if (unused == 0 || unused == size)
                return image;
            byte[] newArray = new byte[array.Length];
            for (int i = 0; i < array.Length; i++)
            {
                newArray[i] = indexof[array[i]];
            }
            image = MakeImage(image.Width, image.Height, newArray, image.PixelFormat);
            image.Palette = newPalette;
            return image;
        }

        public bool[] IsUsed(Bitmap image, bool[] used = null)
        {
            byte[] array = GetArray(image);
            int size = image.Palette.Entries.Length;
            if (used == null)
            {
                used = new bool[size];
                for (int i = 0; i < size; i++)
                {
                    used[i] = false;
                }
            }
            if (size > used.Length)
            {
                bool[] temp = new bool[size];
                for (int i = 0; i < size; i++)
                {
                    if (i < used.Length)
                        temp[i] = used[i];
                    else
                        temp[i] = false;
                }
                used = temp;
            }
            for (int i = 0; i < array.Length; i++)
            {
                used[array[i]] = true;
            }
            return used;
        }

        public byte PaletteSize(Bitmap image)
        {
            byte[] array = GetArray(image);
            byte max = 0;
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] > max)
                    max = array[i];
            }
            return (byte)(max + 1);
        }

        public bool PaletteEquals(ColorPalette parent, Bitmap child)
        {
            bool Match = true;
            int max = PaletteSize(child);
            for (int i = 0; i < max; i++)
            {
                if (parent.Entries[i] != child.Palette.Entries[i])
                    Match = false;
            }
            return Match;
        }

        public ColorPalette CleanPalette(Bitmap image)
        {
            int used = PaletteSize(image);
            ColorPalette temp = image.Palette;
            for (int i = used; i < image.Palette.Entries.Length; i++)
                temp.Entries[i] = Color.FromArgb(0, 0, 0);
            return temp;
        }

        public byte[] GetArray(Bitmap target)
        {
            int Width = target.Width;
            if (target.PixelFormat == PixelFormat.Format1bppIndexed)
                Width = Width / 8;
            if (target.PixelFormat == PixelFormat.Format4bppIndexed)
                Width = Width / 2;
            if ((target.PixelFormat == PixelFormat.Format16bppArgb1555) || (target.PixelFormat == PixelFormat.Format16bppGrayScale) || (target.PixelFormat == PixelFormat.Format16bppRgb555) || (target.PixelFormat == PixelFormat.Format16bppRgb565))
                Width = Width * 2;
            if (target.PixelFormat == PixelFormat.Format24bppRgb)
                Width = Width * 3;
            if ((target.PixelFormat == PixelFormat.Format32bppRgb) || (target.PixelFormat == PixelFormat.Format32bppArgb) || (target.PixelFormat == PixelFormat.Format32bppPArgb))
                Width = Width * 4;
            byte[] array = new byte[Width * target.Height];
            Rectangle rect = new Rectangle(0, 0, target.Width, target.Height);
            BitmapData sourceData = target.LockBits(rect, ImageLockMode.ReadOnly, target.PixelFormat);
            IntPtr scan = sourceData.Scan0;
            Marshal.Copy(scan, array, 0, Width * target.Height);
            target.UnlockBits(sourceData);
            return array;
        }

        public Bitmap MakeImage(int width, int height, byte[] array, PixelFormat format)
        {
            Bitmap image = new Bitmap(width, height, format);
            Rectangle rect = new Rectangle(0, 0, width, height);
            BitmapData imageData = image.LockBits(rect, ImageLockMode.WriteOnly, image.PixelFormat);
            IntPtr scan = imageData.Scan0;
            Marshal.Copy(array, 0, scan, (width * height));
            image.UnlockBits(imageData);
            return image;
        }
    }
}