using System;
using System.Runtime.InteropServices;
using Android.Runtime;
using Android.Views;
using Avalonia.Platform;

namespace Avalonia.Android.Platform.SkiaPlatform
{
    class AndroidFramebuffer : ILockedFramebuffer
    {
        private IntPtr _window;

        public AndroidFramebuffer(Surface surface, double scaling)
        {
            if(surface == null)
                throw new ArgumentNullException(nameof(surface));
            _window = ANativeWindow_fromSurface(JNIEnv.Handle, surface.Handle);
            if (_window == IntPtr.Zero)
                throw new Exception("Unable to obtain ANativeWindow");
            ANativeWindow_Buffer buffer;
            var rc = new ARect()
            {
                right = ANativeWindow_getWidth(_window),
                bottom = ANativeWindow_getHeight(_window)
            };
            Size = new PixelSize(rc.right, rc.bottom);
            ANativeWindow_lock(_window, out buffer, ref rc);

            Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565
                ? PixelFormat.Rgb565 : PixelFormat.Rgba8888;

            RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4);
            Address = buffer.bits;

            Dpi = new Vector(96, 96) * scaling;
        }

        public void Dispose()
        {
            ANativeWindow_unlockAndPost(_window);
            ANativeWindow_release(_window);
            _window = IntPtr.Zero;
            Address = IntPtr.Zero;
        }

        public IntPtr Address { get; set; }
        public PixelSize Size { get; }
        public int RowBytes { get; }
        public Vector Dpi { get; }
        public PixelFormat Format { get; }

        [DllImport("android")]
        internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle);
        [DllImport("android")]
        internal static extern int ANativeWindow_getWidth(IntPtr window);
        [DllImport("android")]
        internal static extern int ANativeWindow_getHeight(IntPtr window);
        [DllImport("android")]
        internal static extern void ANativeWindow_release(IntPtr window);
        [DllImport("android")]
        internal static extern void ANativeWindow_unlockAndPost(IntPtr window);

        [DllImport("android")]
        internal static extern int ANativeWindow_lock(IntPtr window, out ANativeWindow_Buffer outBuffer, ref ARect inOutDirtyBounds);
        public enum AndroidPixelFormat
        {
            WINDOW_FORMAT_RGBA_8888 = 1,
            WINDOW_FORMAT_RGBX_8888 = 2,
            WINDOW_FORMAT_RGB_565 = 4,
        }

        internal struct ARect
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
        
        internal struct ANativeWindow_Buffer
        {
            // The number of pixels that are show horizontally.
            public int width;

            // The number of pixels that are shown vertically.
            public int height;

            // The number of *pixels* that a line in the buffer takes in
            // memory.  This may be >= width.
            public int stride;

            // The format of the buffer.  One of WINDOW_FORMAT_*
            public AndroidPixelFormat format;

            // The actual bits.
            public IntPtr bits;

            // Do not touch.
            uint reserved1;
            uint reserved2;
            uint reserved3;
            uint reserved4;
            uint reserved5;
            uint reserved6;
        }
    }
}
