using System;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Interop;

namespace HandyControl.Data
{
    internal class GlowBitmap : DisposableObject
    {
        internal const int GlowBitmapPartCount = 16;

        private const int BytesPerPixelBgra32 = 4;

        private static readonly CachedBitmapInfo[] _transparencyMasks = new CachedBitmapInfo[GlowBitmapPartCount];

        private readonly InteropValues.BITMAPINFO _bitmapInfo;

        private readonly IntPtr _pbits;

        internal GlowBitmap(IntPtr hdcScreen, int width, int height)
        {
            _bitmapInfo.biSize = Marshal.SizeOf(typeof(InteropValues.BITMAPINFOHEADER));
            _bitmapInfo.biPlanes = 1;
            _bitmapInfo.biBitCount = 32;
            _bitmapInfo.biCompression = 0;
            _bitmapInfo.biXPelsPerMeter = 0;
            _bitmapInfo.biYPelsPerMeter = 0;
            _bitmapInfo.biWidth = width;
            _bitmapInfo.biHeight = -height;

            Handle = InteropMethods.CreateDIBSection(
                hdcScreen,
                ref _bitmapInfo,
                0u,
                out _pbits,
                IntPtr.Zero,
                0u);
        }

        internal IntPtr Handle { get; }

        internal IntPtr DIBits => _pbits;

        internal int Width => _bitmapInfo.biWidth;

        internal int Height => -_bitmapInfo.biHeight;

        protected override void DisposeNativeResources() => InteropMethods.DeleteObject(Handle);

        private static byte PremultiplyAlpha(byte channel, byte alpha) => (byte) (channel * alpha / 255.0);

        internal static GlowBitmap Create(GlowDrawingContext drawingContext, GlowBitmapPart bitmapPart, Color color)
        {
            var orCreateAlphaMask =
                GetOrCreateAlphaMask(bitmapPart);

            var glowBitmap =
                new GlowBitmap(
                    drawingContext.ScreenDC,
                    orCreateAlphaMask.Width,
                    orCreateAlphaMask.Height);

            for (var i = 0; i < orCreateAlphaMask.DIBits.Length; i += BytesPerPixelBgra32)
            {
                var b = orCreateAlphaMask.DIBits[i + 3];
                var val = PremultiplyAlpha(color.R, b);
                var val2 = PremultiplyAlpha(color.G, b);
                var val3 = PremultiplyAlpha(color.B, b);
                Marshal.WriteByte(glowBitmap.DIBits, i, val3);
                Marshal.WriteByte(glowBitmap.DIBits, i + 1, val2);
                Marshal.WriteByte(glowBitmap.DIBits, i + 2, val);
                Marshal.WriteByte(glowBitmap.DIBits, i + 3, b);
            }

            return glowBitmap;
        }

        private static CachedBitmapInfo GetOrCreateAlphaMask(GlowBitmapPart bitmapPart)
        {
            if (_transparencyMasks[(int) bitmapPart] == null)
            {
                var bitmapImage = new BitmapImage(new Uri($"pack://application:,,,/HandyControl;Component/Resources/Images/GlowWindow/{bitmapPart}.png"));

                var array = new byte[BytesPerPixelBgra32 * bitmapImage.PixelWidth * bitmapImage.PixelHeight];
                var stride = BytesPerPixelBgra32 * bitmapImage.PixelWidth;
                bitmapImage.CopyPixels(array, stride, 0);
                bitmapImage.Freeze();

                _transparencyMasks[(int) bitmapPart] =
                    new CachedBitmapInfo(
                        array,
                        bitmapImage.PixelWidth,
                        bitmapImage.PixelHeight);
            }

            return _transparencyMasks[(int) bitmapPart];
        }

        private sealed class CachedBitmapInfo
        {
            internal readonly byte[] DIBits;
            internal readonly int Height;
            internal readonly int Width;

            internal CachedBitmapInfo(byte[] diBits, int width, int height)
            {
                Width = width;
                Height = height;
                DIBits = diBits;
            }
        }
    }
}
