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

namespace Inspectron.Camera.Image
{
    public class ByteImage : IImage
    {

        Byte[] data;
        int step;

        /// <summary>
        /// Initializes a newinstance with the given image dimensions and pixel-data
        /// </summary>
        /// <param name="width">the image width in pixel</param>
        /// <param name="height">the image height in pixel</param>
        /// <param name="channels">the number of color channels (1-4)</param>
        /// <param name="channelDepth">the bit-depth per channel (8 or 16)</param>
        /// <param name="data">pointer to the pixel data in unmanaged memory</param>
        /// <param name="step">the number of bytes per image row</param>
        public ByteImage(int width, int height, int channels,
            Byte[] data, int step)
        {
            Width = width;
            Height = height;
            Channels = channels;

            this.data = data;
            this.step = step;

            ProcessBitmapCache();
        }

        private void ProcessBitmapCache()
        {
            var image = new Bitmap(Width, Height,
                Channels == 1 ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb);
            var lockedDst = image.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly,
                image.PixelFormat);
            Marshal.Copy(data, 0, lockedDst.Scan0, data.Length);
            image.UnlockBits(lockedDst);
            if (Channels == 1)
            {
                image.Palette = _monoPalette;
            }

            _cachedBMP = image;
        }

        static ColorPalette GetGrayScalePalette()
        {
            Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);

            ColorPalette monoPalette = bmp.Palette;

            Color[] entries = monoPalette.Entries;

            for (int i = 0; i < 256; i++)
            {
                entries[i] = Color.FromArgb(i, i, i);
            }

            return monoPalette;
        }

        private static ColorPalette _monoPalette = GetGrayScalePalette();
        public int Width { get; private set; }
        public int Height { get; private set; }
        public int Channels { get; private set; }

        public IImageLock Lock { get { return new ImageLock(this); } }
        private Bitmap _cachedBMP = null;
        public Bitmap Bitmap
        {
            get
            {


                return (Bitmap)_cachedBMP.Clone();
            }
        }

        public byte[] Data
        {
            get => data;
            set => data = value;
        }

        public int Step
        {
            get => step;
            set => step = value;
        }

        class ImageLock : IImageLock
        {
            ByteImage parent;
            GCHandle pinnedArray;
            internal ImageLock(ByteImage parent) { this.parent = parent; }

            public int Step { get { return parent.step; } }

            public IntPtr PixelData
            {
                get
                {
                    pinnedArray = GCHandle.Alloc(parent.data, GCHandleType.Pinned);
                    IntPtr pointer = pinnedArray.AddrOfPinnedObject();
                    return pointer;
                }
            }

            public void Dispose()
            {
                pinnedArray.Free();
            }
        }
        public void Save(string path)
        {
            (new Bitmap(new MemoryStream(data))).Save(path,ImageFormat.Bmp);
        }

        public void Dispose()
        {
            data = null;
        }
    }
}