using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Foundation;
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;

namespace Ryujinx.Graphics.Metal
{
    [SupportedOSPlatform("macos")]
    struct RenderEncoderState
    {
        private readonly MTLDevice _device;
        private readonly MTLFunction? _vertexFunction = null;
        private readonly MTLFunction? _fragmentFunction = null;
        private MTLDepthStencilState? _depthStencilState = null;

        private MTLCompareFunction _depthCompareFunction = MTLCompareFunction.Always;
        private bool _depthWriteEnabled = false;

        private MTLStencilDescriptor? _backFaceStencil = null;
        private MTLStencilDescriptor? _frontFaceStencil = null;

        public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
        public MTLCullMode CullMode = MTLCullMode.None;
        public MTLWinding Winding = MTLWinding.Clockwise;

        public RenderEncoderState(MTLFunction vertexFunction, MTLFunction fragmentFunction, MTLDevice device)
        {
            _vertexFunction = vertexFunction;
            _fragmentFunction = fragmentFunction;
            _device = device;
        }

        public readonly void SetEncoderState(MTLRenderCommandEncoder renderCommandEncoder, MTLVertexDescriptor vertexDescriptor)
        {
            var renderPipelineDescriptor = new MTLRenderPipelineDescriptor
            {
                VertexDescriptor = vertexDescriptor
            };

            if (_vertexFunction != null)
            {
                renderPipelineDescriptor.VertexFunction = _vertexFunction.Value;
            }

            if (_fragmentFunction != null)
            {
                renderPipelineDescriptor.FragmentFunction = _fragmentFunction.Value;
            }

            var attachment = renderPipelineDescriptor.ColorAttachments.Object(0);
            attachment.SetBlendingEnabled(true);
            attachment.PixelFormat = MTLPixelFormat.BGRA8Unorm;
            attachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha;
            attachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
            attachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha;
            attachment.DestinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;

            var error = new NSError(IntPtr.Zero);
            var pipelineState = _device.NewRenderPipelineState(renderPipelineDescriptor, ref error);
            if (error != IntPtr.Zero)
            {
                Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
            }

            renderCommandEncoder.SetRenderPipelineState(pipelineState);
            renderCommandEncoder.SetCullMode(CullMode);
            renderCommandEncoder.SetFrontFacingWinding(Winding);

            if (_depthStencilState != null)
            {
                renderCommandEncoder.SetDepthStencilState(_depthStencilState.Value);
            }
        }

        public MTLDepthStencilState UpdateStencilState(MTLStencilDescriptor backFace, MTLStencilDescriptor frontFace)
        {
            _backFaceStencil = backFace;
            _frontFaceStencil = frontFace;

            _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = _depthCompareFunction,
                DepthWriteEnabled = _depthWriteEnabled,
                BackFaceStencil = _backFaceStencil.Value,
                FrontFaceStencil = _frontFaceStencil.Value
            });

            return _depthStencilState.Value;
        }

        public MTLDepthStencilState UpdateDepthState(MTLCompareFunction depthCompareFunction, bool depthWriteEnabled)
        {
            _depthCompareFunction = depthCompareFunction;
            _depthWriteEnabled = depthWriteEnabled;

            _depthStencilState = _device.NewDepthStencilState(new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = _depthCompareFunction,
                DepthWriteEnabled = _depthWriteEnabled,
                BackFaceStencil = _backFaceStencil.Value,
                FrontFaceStencil = _frontFaceStencil.Value
            });

            return _depthStencilState.Value;
        }
    }
}
