using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace ImageBlockz.Core
{
    /// <summary>
    /// Class for faster image processing (SetPixel, GetPixel) using \unsafe code. 
    /// </summary>
    public class BitmapWrapper
    {
        private Bitmap bmp;
        private BitmapData bmpData;

        /// <summary>
        /// Create new wrapper for faster image processing from input bitmap.
        /// </summary>
        /// <param name="bmp">Input Bitmap.</param>
        public BitmapWrapper(Bitmap bmp)
        {
            if (bmp == null)
            {
                throw new ArgumentNullException("Bitmap is null.");
            }

            this.bmp = bmp;
        }

        /// <summary>
        /// Lock all bits in bitmap data which can be changed programmatically.
        /// </summary>
        public void Lock()
        {
            if (bmpData != null)
            {
                throw new InvalidOperationException("Image is alredy locked");
            }

            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

            bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        }

        /// <summary>
        /// Unlock this wrapper (bitmap) from system memory.
        /// </summary>
        public void Unlock()
        {
            if (bmpData == null)
            {
                throw new InvalidOperationException("Image must be locked");
            }

            bmp.UnlockBits(bmpData);

            bmpData = null;
        }

        /// <summary>
        /// This method gets <see cref="Color"/> from exact <see cref="BitmapWrapper"/> coordiantes (same as <see cref="Bitmap"/>).
        /// </summary>
        /// <param name="x">Horizontal coordinate in <see cref="BitmapWrapper"/>.</param>
        /// <param name="y">Vertical coordinate in <see cref="BitmapWrapper"/>.</param>
        /// <returns><see cref="Color"/> of pixel.</returns>
        public unsafe Color GetPixel(int x, int y)
        {
            Color color;

            byte* pColor = GetPixelPointer(x, y);

            color = Color.FromArgb(pColor[2], pColor[1], pColor[0]);


            return color;
        }

        /// <summary>
        /// This method sets <see cref="Color"/> to exact <see cref="BitmapWrapper"/> coordiantes (same as <see cref="Bitmap"/>).
        /// </summary>
        /// <param name="color"><see cref="Color"/> of pixel.</param>
        /// <param name="x">Horizontal coordinate in <see cref="BitmapWrapper"/>.</param>
        /// <param name="y">Vertical coordinate in <see cref="BitmapWrapper"/>.</param>
        public unsafe void SetPixel(Color color, int x, int y)
        {
            byte* pColor = GetPixelPointer(x, y);

            pColor[0] = color.B;
            pColor[1] = color.G;
            pColor[2] = color.R;

        }

        private unsafe byte* GetPixelPointer(int x, int y)
        {
            int stride = bmpData.Stride;
            int offset = y * stride + 3 * x;


            byte* pData = (byte*)bmpData.Scan0.ToPointer();
            byte* pColor = pData + offset;

            return pColor;
        }
    }
}
