using System;
using System.IO;
using System.Runtime.InteropServices;
using Quik.Media;
using Quik.Media.Color;
using Quik.Stb;

namespace Quik.Media.Stb
{
    public unsafe class QImageStbi : QImage
    {
        private readonly StbImage image;
        private ImageBuffer buffer;

        public override int Width => image.Width;

        public override int Height => image.Height;

        public override int Depth => 1;
        public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format);

        public QImageStbi(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 QImageFormat Stb2QImageFormat(StbiImageFormat src)
        {
            switch (src)
            {
            case StbiImageFormat.Grey:      return QImageFormat.RedU8;
            case StbiImageFormat.Rgb:       return QImageFormat.RgbU8;
            case StbiImageFormat.Rgba:      return QImageFormat.RgbaU8;
            case StbiImageFormat.GreyAlpha: return QImageFormat.RaU8;
            default:                        return QImageFormat.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 ImageBuffer(options.Format, Width, Height);
            QImageLock dst = buffer.Lock();

            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.Unlock();
        }

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

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