import Metal
import MetalKit

public class AuraStillRender {
    
    public struct RenderConfig {
        public let width: Int
        public let height: Int
        public let colorSet: SunoTriColorSet
        public let scale: Float
        public let seed: Float
        
        public init(
            width: Int,
            height: Int,
            colorSet: SunoTriColorSet,
            scale: Float,
            seed: Float) {
                
            self.width = width
            self.height = height
            self.colorSet = colorSet
            self.scale = scale
            self.seed = seed
        }
        
        public init(
            width: Int,
            height: Int,
            colorA: ADVector3,
            colorB: ADVector3,
            colorC: ADVector3,
            scale: Float,
            seed: Float) {
            
            self.width = width
            self.height = height
            self.colorSet = .custom(
                .custom(colorA.x, colorA.y, colorA.z),
                .custom(colorB.x, colorB.y, colorB.z),
                .custom(colorC.x, colorC.y, colorC.z)
            )
            self.scale = scale
            self.seed = seed
        }
    }
    
    public static func renderStandardAuraToStillPNG(_ config: RenderConfig) throws -> URL? {
            
        let metalTextureRender = try AuraStillRender
            .renderStill(
                width: config.width,
                height: config.height,
                colorA: config.colorSet.colorA.floatThree,
                colorB: config.colorSet.colorB.floatThree,
                colorC: config.colorSet.colorC.floatThree,
                scale: config.scale,
                seed: config.seed
            )
        return try StillCaptureUtility.png(texture: metalTextureRender)
    }
    
    public static func renderStandardAuraToStill(_ config: RenderConfig) throws -> CGImage? {
            
        let metalTextureRender = try AuraStillRender
            .renderStill(
                width: config.width,
                height: config.height,
                colorA: config.colorSet.colorA.floatThree,
                colorB: config.colorSet.colorB.floatThree,
                colorC: config.colorSet.colorC.floatThree,
                scale: config.scale,
                seed: config.seed
            )
        return try StillCaptureUtility.cgImage(texture: metalTextureRender)
    }
}

private extension AuraStillRender {
    
    static func renderStill(
        width: Int,
        height: Int,
        colorA: ADVector3,
        colorB: ADVector3,
        colorC: ADVector3,
        scale: Float,
        seed: Float) -> MTLTexture? {
            
        var size: ADVector2 {
            return ADVector2(
                x: Float(max(1, width)),
                y: Float(max(1, height))
            )
        }
            
        let noiseRenderable = Simplex3DAuraRenderable()
        noiseRenderable.config = .init(
            displaySize: size,
            fractalNoiseX: .zero,
            fractalNoiseY: .zero,
            fractalNoiseZ: seed,
            fractalUniformScale: 0.5 * scale,
            grainNoiseZ: seed,
            grainNoiseScale: 5.0 * scale,
            circleMaskRadius: 100.0,    // Just means will not mask
            backgroundColor: SunoColorPalette.backgroundDotCom.floatThree,
            colorA: colorA,
            colorB: colorB,
            colorC: colorC,
            mixPointStart:  -0.2,       // Negative value here just means won't be used
            mixPointBackground: -0.1,   // Negative value here just means won't be used
            mixPointBackgroundA: 0.0,
            mixPointAB: 0.2,
            mixPointBC: 0.4,
            mixPointEnd: 2.0
        )
        
        let renderableLayers: [Renderable] = [
            noiseRenderable
        ]
        
        let renderHelper = OfflineRenderHelper(width: width, height: height)
        renderHelper.commitRenderableLayers(layers: renderableLayers)
        return renderHelper.lastRender
    }
}
