package scientific.matplot

import std.math.*
import std.collection.*
import std.io.*
import std.fs.*
import std.unittest.*
import std.unittest.testmacro.*

import scientific.numbers.*
import scientific.linear.*
import scientific.stats.normal.randn
import scientific.stats.normal.rand

foreign func c_area(
    y: CPointer<Unit>, row: Int64, col: Int64, base: Float64, stacked: Bool): CPointer<CPointer<Unit>>
foreign func c_face_color(
    area: CPointer<Unit>, r: Float32, g: Float32, b: Float32, a: Float32): CPointer<Unit>

public class Area <: Line {
    /* Input is a pointer to matplot::filled_area. */
    init(ptr: CPointer<Unit>) {
        super(ptr)
    }

    public func face_color(r: Float32, g: Float32, b: Float32, a: Float32): Area {
        this.ptr = unsafe { c_face_color(this.ptr, r, g, b, a) }
        return this
    }

    public override func line_style(line_style: String): Area {
        var cstr_line_style = unsafe { LibC.mallocCString(line_style) }
        this.ptr = unsafe { c_line_style(this.ptr, cstr_line_style) }
        unsafe { LibC.free(cstr_line_style) }
        return this
    }
}

public func area(x: Matrix<Float64>, base!: Float64 = 0.0, stacked!: Bool = true): Array<Area> {
    let row = x.getRows()
    let col = x.getCols()
    let handles = unsafe { c_area(x.ptr, row, col, base, stacked) }
    let res = ArrayList<Area>()
    for (i in 0..row) {
        res.add(Area(unsafe {handles.read(i)}))
    }
    return res.toArray()
}

public func testArea1() {
    let Y = matrix<Float64>(
        [[1.0, 3.0, 1.0, 2.0],
         [5.0, 2.0, 5.0, 6.0],
         [3.0, 7.0, 3.0, 1.0]])
    let f = gcf()
    f.width(f.width() * 2)

    subplot(1, 2, 0)
    area(Y)
    title("Stacked")

    subplot(1, 2, 1)
    area(Y, stacked: false)
    title("Not stacked")
    save("./tests/imgs/area/area_1.svg", "svg")
    f.width(f.width() / 2)
    clear()
}

public func testArea2() {
    let Y = matrix<Float64>(
        [[1.0, 3.0, 1.0, 2.0],
         [5.0, 2.0, 5.0, 6.0],
         [3.0, 7.0, 3.0, 1.0]])
    let f = gcf()
    f.width(f.width() * 2)

    subplot(1, 2, 0)
    area(Y, base: -4.0)
    title("Stacked")

    subplot(1, 2, 1)
    area(Y, base: -4.0, stacked: false)
    title("Not stacked")
    save("./tests/imgs/area/area_2.svg", "svg")
    f.width(f.width() / 2)
    clear()
}

public func testArea3() {
    let Y = matrix<Float64>(
        [[1.0, 3.0, 1.0, 2.0],
         [5.0, 2.0, 5.0, 6.0],
         [3.0, 7.0, 3.0, 1.0]])
    let f = gcf()
    f.width(f.width() * 2)

    subplot(1, 2, 0)
    let h1: Array<Area> = area(Y, base: -4.0)
    h1[0].line_style(":").face_color(0.0, 0.0, 0.25, 0.25)
    h1[1].line_style(":").face_color(0.0, 0.0, 0.5, 0.5)
    h1[2].line_style(":").face_color(0.0, 0.0, 0.75, 0.75)
    title("Stacked")

    subplot(1, 2, 1)
    let h2: Array<Area> = area(Y, base: -4.0, stacked: false)
    h2[0].line_style(":").face_color(0.2, 0.0, 0.25, 0.25)
    h2[1].line_style(":").face_color(0.2, 0.0, 0.5, 0.5)
    h2[2].line_style(":").face_color(0.2, 0.0, 0.75, 0.75)
    title("Not stacked")
    save("./tests/imgs/area/area_3.svg", "svg")
    f.width(f.width() / 2)
    clear()
}

public func testArea4() {
    let Y = matrix<Float64>(
        [[1.0, 3.0, 4.0, 0.0],
         [2.0, 5.0, 4.0, 7.0],
         [6.0, 4.0, 5.0, 3.0]])
    let f = gcf()
    f.width(f.width() * 2)

    subplot(1, 2, 0)
    area(Y, base: -4.0)
    title("Stacked")

    subplot(1, 2, 1)
    area(Y, base: -4.0, stacked: false)
    title("Not stacked")
    save("./tests/imgs/area/area_4.svg", "svg")
    f.width(f.width() / 2)
    clear()
}

public func testArea() {
    print("  + testArea\n")
    testArea1()
    testArea2()
    testArea3()
    testArea4()
}