//
//  PlaygroundTestShaderView.swift
//  Adamantium
//
//  Created by Sunny Uppal on 11/25/24.
//
import SwiftUI
import UIKit
import Metal
import MetalKit

public struct PlaygroundTestShaderView: UIViewRepresentable {
    @ObservedObject var coordinator: Coordinator
    
    public init(coordinator: Coordinator) {
        self.coordinator = coordinator
    }
    
    public func makeCoordinator() -> Coordinator {
        return coordinator
    }
    
    public func makeUIView(context: Context) -> some UIView {
        let mtkView = MTKView()
        mtkView.delegate = context.coordinator
        mtkView.preferredFramesPerSecond = 60
        mtkView.device = Adamantium.sharedDevice
        
        mtkView.framebufferOnly = false
        mtkView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
        mtkView.drawableSize = mtkView.frame.size
        mtkView.enableSetNeedsDisplay = true
        mtkView.isPaused = false
        return mtkView
    }
    
    public func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
    public class Coordinator: NSObject, ObservableObject, MTKViewDelegate {
        
        var metalDevice: MTLDevice?
        var metalCommandQueue: MTLCommandQueue?
        var lastTime: Date?
        var lastSize: CGSize = .init(width: 1.0, height: 1.0)
        
        private var _lastRender: MTLTexture?
        private let lastRenderQueue = DispatchQueue(label: "com.adamantium.lastRenderQueue")
        public var onDrawableSizeUpdate: (CGSize) -> MTLTexture? = { _ in return nil }
        
        var lastRender: MTLTexture? {
            get {
                lastRenderQueue.sync {
                    _lastRender
                }
            }
            set {
                lastRenderQueue.async {
                    self._lastRender = newValue
                }
            }
        }
        
        public override init() {
            self.metalDevice = Adamantium.sharedDevice
            self.metalCommandQueue = Adamantium.sharedCommandQueue
            super.init()
        }
        
        public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
            lastSize = size
            lastRender = onDrawableSizeUpdate(size)
        }
        
        public func forceDrawUpdateWithLastSize() {
            lastRender = onDrawableSizeUpdate(lastSize)
        }
        
        public func draw(in view: MTKView) {
            var size: ADVector2 {
                return ADVector2(
                    x: Float(view.drawableSize.width),
                    y: Float(view.drawableSize.height)
                )
            }
            
            let deltaTime: TimeInterval
            if let lastTime = lastTime {
                deltaTime = Date.now.timeIntervalSince(lastTime)
            } else {
                deltaTime = .zero
            }
            
            lastTime = .now
            
            guard
                let commandBuffer = Adamantium.sharedCommandQueue?.makeCommandBuffer(),
                let currentDrawable = view.currentDrawable,
                let renderDescriptor = view.currentRenderPassDescriptor,
                let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderDescriptor)
            else { return }

            render(size, renderEncoder: renderEncoder)
            
            renderEncoder.endEncoding()
            commandBuffer.present(currentDrawable)
            commandBuffer.commit()
        }
    }
}

private extension PlaygroundTestShaderView.Coordinator {
    
    func render(_ size: ADVector2, renderEncoder: MTLRenderCommandEncoder) {
        let passRenderable = UVTexturePassRenderable()
        passRenderable.inputTexture = lastRender
        passRenderable.render(encoder: renderEncoder)
    }
}
