import CoreML
import Foundation
import SileroCoreML

private final class ChanoraSileroVadBox {
    let vad: SileroVADRunner

    init() throws {
        let configuration = MLModelConfiguration()
        vad = try SileroVADRunner(configuration: configuration)
    }
}

private let chanoraSileroErrorLock = NSLock()
private var chanoraSileroLastError = ""

private func setChanoraSileroLastError(_ message: String) {
    chanoraSileroErrorLock.lock()
    chanoraSileroLastError = message
    chanoraSileroErrorLock.unlock()
}

@_cdecl("chanora_silero_vad_create")
public func chanoraSileroVadCreate() -> UnsafeMutableRawPointer? {
    do {
        let box = try ChanoraSileroVadBox()
        return Unmanaged.passRetained(box).toOpaque()
    } catch {
        setChanoraSileroLastError(String(describing: error))
        return nil
    }
}

@_cdecl("chanora_silero_vad_destroy")
public func chanoraSileroVadDestroy(_ handle: UnsafeMutableRawPointer?) {
    guard let handle else { return }
    Unmanaged<ChanoraSileroVadBox>.fromOpaque(handle).release()
}

@_cdecl("chanora_silero_vad_reset")
public func chanoraSileroVadReset(_ handle: UnsafeMutableRawPointer?) -> Int32 {
    guard let handle else {
        setChanoraSileroLastError("SileroVAD handle is null")
        return -1
    }
    let box = Unmanaged<ChanoraSileroVadBox>.fromOpaque(handle).takeUnretainedValue()
    box.vad.reset()
    return 0
}

@_cdecl("chanora_silero_vad_process")
public func chanoraSileroVadProcess(
    _ handle: UnsafeMutableRawPointer?,
    _ samples: UnsafePointer<Float>?,
    _ sampleCount: Int,
    _ probabilityOut: UnsafeMutablePointer<Float>?
) -> Int32 {
    guard let handle else {
        setChanoraSileroLastError("SileroVAD handle is null")
        return -1
    }
    guard let samples else {
        setChanoraSileroLastError("SileroVAD samples pointer is null")
        return -2
    }
    guard let probabilityOut else {
        setChanoraSileroLastError("SileroVAD probability output pointer is null")
        return -3
    }
    guard sampleCount == SileroVADRunner.chunkSize else {
        setChanoraSileroLastError("SileroVAD expected \(SileroVADRunner.chunkSize) samples, got \(sampleCount)")
        return -4
    }

    let box = Unmanaged<ChanoraSileroVadBox>.fromOpaque(handle).takeUnretainedValue()
    do {
        let chunk = Array(UnsafeBufferPointer(start: samples, count: sampleCount))
        probabilityOut.pointee = try box.vad.process(chunk)
        return 0
    } catch {
        setChanoraSileroLastError(String(describing: error))
        return -5
    }
}

@_cdecl("chanora_silero_vad_last_error")
public func chanoraSileroVadLastError() -> UnsafeMutablePointer<CChar>? {
    chanoraSileroErrorLock.lock()
    let message = chanoraSileroLastError
    chanoraSileroErrorLock.unlock()
    return strdup(message)
}

@_cdecl("chanora_silero_vad_free_string")
public func chanoraSileroVadFreeString(_ string: UnsafeMutablePointer<CChar>?) {
    guard let string else { return }
    free(string)
}
