//
//  IntegrationPlotter.swift
//  ToneShaperPreview
//
//  Created by Joseph Pagliaro on 12/7/23.
//

import Accelerate

class IntegrationPlotter {
    
    var integrand_points:[CGPoint] = [] // cosine
    var integral:[Double] = []
    
    let N:Int
    let delta:Double
    
    init() {
        
        N = 500
        delta = 1.0/Double(N)
        
        for i in 0...N {
            let t = Double(i) * delta
            integrand_points.append(CGPoint(x: t, y: cos(2.0 * .pi * t)))
        }
        
        let values = integrand_points.map { p in
            Double(p.y)
        }
        
        integral = integrate(samples: values)
        
    }
    
    /*
     Extend a function by interpolating points
     */
    func interpolate(t: Double, points: [CGPoint]) -> Double {
            // Check if t is out of range
        if t < points[0].x || t > points[points.count - 1].x {
            return 0.0
        }
        
            // Find the largest index i such that c[i].x <= t
        var i = 0
        while i < points.count - 1 && points[i + 1].x <= t {
            i += 1
        }
        
            // Perform linear interpolation
        let x0 = points[i].x
        let y0 = points[i].y
        
        if i < points.count - 1 {
            let x1 = points[i + 1].x
            let y1 = points[i + 1].y
            let interpolatedValue = y0 + (y1 - y0) * (t - x0) / (x1 - x0)
            return interpolatedValue
        } else {
            return y0
        }
    }
    
    func integrate(samples:[Double]) -> [Double] {
        
        let sampleCount = samples.count
        var step = 1.0 / Double(sampleCount)
        
        var result = [Double](repeating: 0.0, count: sampleCount)
        
        vDSP_vsimpsD(samples, 1, &step, &result, 1, vDSP_Length(sampleCount))
        
        return result
    }
    
        // sampled function cos(2 pi x) - red
    func instantaneous_frequency(_ t:CGFloat) -> CGFloat {
        return interpolate(t: t, points: integrand_points)
    }
    
        // numerical integral using sampled function cos(2 pi x) - black
        // ∫cos(2 pi x) = sin(2 pi x)/(2 pi)
    func sampling_argumemt(_ t:CGFloat) -> CGFloat { 
        let i = Int((t / delta).rounded(.down))
        return integral[i]
    }
    
        // actual integral cos(2 pi x) - white, dashed
        // ∫cos(2 pi x) = sin(2 pi x)/(2 pi)    
    func actual_integral(_ t:CGFloat) -> CGFloat { 
        return sin(2 * .pi * t) / (2 * .pi)
    }
}
