using System;
using System.IO;
using Dashboard.Media.Color;
using ReFuel.Stb;

namespace Dashboard.Media.Defaults
{
    public unsafe class ImageStbi : Image
    {
        private readonly StbImage image;
        private QImageBuffer buffer;
        private bool isSdf = false;

        public override int Width => image.Width;

        public override int Height => image.Height;

        public override int Depth => 1;
        public override bool IsSdf => isSdf;
        public override ImageFormat InternalFormat => Stb2QImageFormat(image.Format);

        public ImageStbi(Stream source)
        {
            // According to the stbi documentation, only a specific type of PNG
            // files are premultiplied out of the box (iPhone PNG). Take the
            // precision loss L and move on.
            StbImage.FlipVerticallyOnLoad = true;
            StbImage.UnpremultiplyOnLoad = true;

            image = StbImage.Load(source);
        }

        public static ImageFormat Stb2QImageFormat(StbiImageFormat src)
        {
            switch (src)
            {
            case StbiImageFormat.Grey:      return ImageFormat.RedU8;
            case StbiImageFormat.Rgb:       return ImageFormat.RgbU8;
            case StbiImageFormat.Rgba:      return ImageFormat.RgbaU8;
            case StbiImageFormat.GreyAlpha: return ImageFormat.RaU8;
            default:                        return ImageFormat.Undefined;
            }
        }

        public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
        {
            if (options.MipLevel > 0) throw new Exception("This image has no mip levels.");

            buffer?.Dispose();
            buffer = new QImageBuffer(options.Format, Width, Height);
            buffer.LockBits2d(out QImageLock dst, QImageLockOptions.Default);

            byte *srcPtr = (byte*)image.ImagePointer;
            QImageLock src = new QImageLock(InternalFormat, Width, Height, 1, (IntPtr)srcPtr);
            FormatConvert.Convert(dst, src);

            if (options.Premultiply)
            {
                FormatConvert.Premultiply(dst);
            }

            imageLock = dst;
        }

        public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
        {
            LockBits2d(out imageLock, options);
        }

        public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
        {
            if (depth != 1) throw new ArgumentOutOfRangeException(nameof(depth));

            LockBits2d(out imageLock, options);
        }

        public override void UnlockBits()
        {
            buffer.UnlockBits();
        }

        public void SdfHint(bool value = true)
        {
            isSdf = value;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                buffer?.Dispose();
                image.Dispose();
            }
        }
    }
}