package scientific.matplot

import std.math.*
import std.unittest.*
import std.unittest.testmacro.*

import scientific.numbers.*
import scientific.linear.*
import scientific.stats.random.*
import scientific.stats.normal.*

/* Type for theta, rho, x, y: Float64 */
foreign func c_polarplot(
    theta: CPointer<Unit>, theta_len: Int64,
    rho: CPointer<Unit>, rho_len: Int64, line_spec: CString): CPointer<Unit>

foreign func c_polarplot_rho(rho: CPointer<Unit>, rho_len: Int64, line_spec: CString): CPointer<Unit>

foreign func c_compass(x: CPointer<Unit>, x_len: Int64,
    y: CPointer<Unit>, y_len: Int64, line_spec: CString): Unit

foreign func c_ezpolar(f: CString): Unit

public func polarplot(theta: Vector<Float64>, rho: Vector<Float64>, line_spec!: String=""):Line {
    let size = theta.size()
    var cstr_line_spec = unsafe { LibC.mallocCString(line_spec) }
    let handle = unsafe { c_polarplot(theta.ptr, size, rho.ptr, size, cstr_line_spec) }
    unsafe { LibC.free(cstr_line_spec) }
    return Line(handle)
}

public func polarplot(rho: Vector<Float64>, line_spec!: String=""):Line {
    var cstr_line_spec = unsafe { LibC.mallocCString(line_spec) }
    let handle = unsafe { c_polarplot_rho(rho.ptr, rho.size(), cstr_line_spec) }
    unsafe { LibC.free(cstr_line_spec) }
    return Line(handle)
}

public func compass(x: Vector<Float64>, y: Vector<Float64>, line_spec!: String="") {
    var cstr_line_spec = unsafe { LibC.mallocCString(line_spec) }
    unsafe { c_compass(x.ptr, x.size(), y.ptr, y.size(), cstr_line_spec) }
    unsafe { LibC.free(cstr_line_spec) }
}

public func ezpolar(function: String) {
    var cstr_function = unsafe { LibC.mallocCString(function) }
    unsafe { c_ezpolar(cstr_function) }
    unsafe { LibC.free(cstr_function) }
}

public func ezpolar(f: (Float64)->Float64) {
    let theta = linspace(0.0, 2.0 * Float64.getPI(), num: 628)
    let rho = theta.apply(f)

    polarplot(theta, rho)
}

public func testPolarPlot1() {
    let theta = linspace(0.0, 2.0 * Float64.getPI(), num: 628)
    let rho = theta.apply({t:Float64 => sin(2.0 * t) * cos(2.0 * t)})

    polarplot(theta, rho)
    save("./tests/imgs/polar/polarPlot_1.svg", "svg")
    clear()
}

public func testPolarPlot2() {
    let theta_degree = linspace(0.0, 360.0, num: 50)
    let rho = theta_degree.apply({t => 0.0005 * t})
    let theta_rad = theta_degree.apply({t => t * Float64.getPI() / 180.0})

    polarplot(theta_rad, rho)
    save("./tests/imgs/polar/polarPlot_2.svg", "svg")
    clear()
}

public func testPolarPlot3() {
    let theta = linspace(0.0, 6.0 * Float64.getPI())
    let rho1 = theta.apply({t => t / 10.0})
    let rho2 = theta.apply({t => t / 12.0})

    polarplot(theta, rho1)
    hold(true)
    polarplot(theta, rho2, line_spec: "--")
    save("./tests/imgs/polar/polarPlot_3.svg", "svg")
    clear()
}

public func testPolarPlot4() {
    let rho = linspace(10.0, 70.0, num: 13)
    polarplot(rho, line_spec: "-o")
    save("./tests/imgs/polar/polarPlot_4.svg", "svg")
    clear()
}

public func testPolarPlot5() {
    let theta = linspace(0.0, 2.0 * Float64.getPI())
    let rho = theta.apply({t => sin(t)})
    polarplot(theta, rho)
    gca().r_axis().limits(-1.0, 1.0)
    save("./tests/imgs/polar/polarPlot_5.svg", "svg")
    clear()
}

public func testPolarPlot6() {
    let theta = linspace(0.0, 2.0 * Float64.getPI(), num:25)
    let rho = theta.apply({t => t * 2.0})
    polarplot(theta, rho, line_spec:"r-o")
    save("./tests/imgs/polar/polarPlot_6.svg", "svg")
    clear()
}

public func testCompass1() {
    var r1 = Random(0)
    var r2 = Random(1)
    let x = rand(r1, 20, -1.0, 1.0)
    let y = rand(r2, 20, -1.0, 1.0)

    compass(x, y)
    save("./tests/imgs/polar/compass_1.svg", "svg")
    clear()
}

public func testPolarFunc1() {
    ezpolar("1 + cos(t)")
    save("./tests/imgs/polar/polarFunction_1.svg", "svg")
    clear()
}

public func testPolarFunc2() {
    ezpolar({t => 1.0 + cos(t)})
    save("./tests/imgs/polar/polarFunction_2.svg", "svg")
    clear()
}

public func testPolarPlot() {
    print("  + testPolarPlot\n")
    testPolarPlot1()
    testPolarPlot2()
    testPolarPlot3()
    testPolarPlot4()
    testPolarPlot5()
    testPolarPlot6()
    testCompass1()
    testPolarFunc1()
    testPolarFunc2()
}