using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Graphics.Gpu
{
    using Texture = Image.Texture;
    /// 
    /// GPU image presentation window.
    ///  
    public class Window
    {
        private readonly GpuContext _context;
        /// 
        /// Texture presented on the window.
        ///  
        private struct PresentationTexture
        {
            /// 
            /// Texture information.
            ///  
            public TextureInfo Info { get; }
            /// 
            /// Physical memory locations where the texture data is located.
            ///  
            public MultiRange Range { get; }
            /// 
            /// Texture crop region.
            ///  
            public ImageCrop Crop { get; }
            /// 
            /// Texture acquire callback.
            ///  
            public Action AcquireCallback { get; }
            /// 
            /// Texture release callback.
            ///  
            public Action ReleaseCallback { get; }
            /// 
            /// User defined object, passed to the various callbacks.
            ///  
            public object UserObj { get; }
            /// 
            /// Creates a new instance of the presentation texture.
            ///  
            ///  acquireCallback,
                Action             releaseCallback,
                object                     userObj)
            {
                Info            = info;
                Range           = range;
                Crop            = crop;
                AcquireCallback = acquireCallback;
                ReleaseCallback = releaseCallback;
                UserObj         = userObj;
            }
        }
        private readonly ConcurrentQueue _frameQueue;
        private int _framesAvailable;
        /// 
        /// Creates a new instance of the GPU presentation window.
        ///  
        /// ();
        }
        /// 
        /// Enqueues a frame for presentation.
        /// This method is thread safe and can be called from any thread.
        /// When the texture is presented and not needed anymore, the release callback is called.
        /// It's an error to modify the texture after calling this method, before the release callback is called.
        ///  
        ///  acquireCallback,
            Action             releaseCallback,
            object                     userObj)
        {
            FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
            TextureInfo info = new TextureInfo(
                0UL,
                width,
                height,
                1,
                1,
                1,
                1,
                stride,
                isLinear,
                gobBlocksInY,
                1,
                1,
                Target.Texture2D,
                formatInfo);
            int size = SizeCalculator.GetBlockLinearTextureSize(
                width,
                height,
                1,
                1,
                1,
                1,
                1,
                bytesPerPixel,
                gobBlocksInY,
                1,
                1).TotalSize;
            MultiRange range = new MultiRange(address, (ulong)size);
            _frameQueue.Enqueue(new PresentationTexture(info, range, crop, acquireCallback, releaseCallback, userObj));
        }
        /// 
        /// Presents a texture on the queue.
        /// If the queue is empty, then no texture is presented.
        ///  
        /// 
        /// Indicate that a frame on the queue is ready to be acquired.
        ///  
        public void SignalFrameReady()
        {
            Interlocked.Increment(ref _framesAvailable);
        }
        /// 
        /// Determine if any frames are available, and decrement the available count if there are.
        ///  
        /// True if a frame is available, false otherwise 
        public bool ConsumeFrameAvailable()
        {
            if (Interlocked.CompareExchange(ref _framesAvailable, 0, 0) != 0)
            {
                Interlocked.Decrement(ref _framesAvailable);
                return true;
            }
            return false;
        }
    }
}