# "Learning from Time-Changing Data with Adaptive Windowing"
#
# - v.0.3 (28 Jul 2017): Preparing the journal version of Pccf
#
# - v.0.2 (14 Dec 2016) : While doing experiments for change detection in multi-sensor settings ./201612-pccf3-code
#
#    Fixed the bug while checking the main condition
#
# - v.0.1 (12 Dec 2016) : While doing experiments for change detection in multi-sensor settings ./201612-pccf3-code
#
#    First implementation of `adwin' to use in multi-sensor settings
#    with Kalman Filter.
#
# https://github.com/abifet/adwin/blob/master/Java/ADWIN.java
#
using Distributions
#using Plots
#plotlyjs()

function scale01(sig)
    mx = maximum(sig)
    mn = minimum(sig)
    (sig - mn) / (mx-mn)
end

"Calc. ecut parameter for the partition of W = W0 dot W1"
function calcEcut(n0, n1, delta)
    @assert n0 >= 1
    @assert n1 >= 1
    m = 1.0/(1.0/n0 + 1.0/n1)
    deltaPrime = delta/(n0+n1)
    ecut = sqrt((1.0/(2.0*m)) * log(4/deltaPrime))
end

"Eq. 3.1"
function calcEcutEq31(w, n0, n1, delta)
    @assert n0 >= 1
    @assert n1 >= 1
    @assert n0 + n1 == length(w)
    sigma = std(w)
    m = 1.0/(1.0/n0 + 1.0/n1)
    deltaPrime = delta/(n0+n1)
    A = sqrt( (2/m) * sigma^2 * log(2/deltaPrime) )
    B = (2.0/(3.0 * m)) * log(2/deltaPrime)
    ecut = A + B
end

function recReduce!(x::Array{Float64,1},
                     ecut_custom::Float64,
                     min_len::Int=3,
                     rec_calls::Int = 1)
    n = length(x)
    if n <= min_len
        return x
    end
    cond_flag = true
    for k = 2:n-1
        cond = abs(mean(x[1:k])-mean(x[(k+1):end])) < ecut_custom
        if !cond
            cond_flag = false
            break
        end
    end
    if cond_flag
        return x
    else
        recReduce!(x[1:(end-1)], ecut_custom, min_len, rec_calls+1)
    end
end

function adwin_naive(sig::Array{Float64,1}; ecut_custom=0.5, min_len=10)
    ys = scale01(sig)
    n = length(ys)
    w::Array{Float64,1} = [ys[1]]
    r::Array{Float64,1}= [ys[1]]
    ws::Array{Int64,1} = [1] # widths
    for t=2:n
        insert!(w, 1, ys[t])
        w = recReduce!(w, ecut_custom, min_len)
        push!(ws, length(w))
    end
    ws
end

function genSigWithSlope(N, k=0.7)
    slope_part1 = randn(N)  .+ [1:N;] * k
    slope_part2 = randn(N)  .- [1:N;] * k
    sig = [rand(N);
           slope_part1;
           randn(N) .+ maximum(slope_part1);
           randn(floor(Int, 1.5*N))
           #slope_part2;
           #randn(N) .+ minimum(slope_part2)
           ]
    sig
end

"read output in inotebook and plot"
function demo_adwin_naive()
    srand(1234)
    N = 15
    settings = Dict("set1" => Dict("step" => 5.0, "ecut" => 0.5),
                    "set2" => Dict("step" => 2.5, "ecut" => 0.5/1.7),
                    "set3" => Dict("step" => 1.0, "ecut" => 0.5/1.7),
                    "set4_slope_sig" => Dict("step" => 1.0, "ecut" => 0.5/2.5)
                    )

    usesettings = settings["set4_slope_sig"]

    insig = scale01([randn(N); randn(N) + usesettings["step"]; randn(N)])
    insig2 = genSigWithSlope(N, 0.3)

    dat = insig2
    r = adwin_naive(dat, min_len=2, ecut_custom = usesettings["ecut"])

    # scale01(r), dat
    r, dat
end
# r = demo_adwin_naive()

function extractChps(x)
    chps::Array{Int,1}=[]
    n=length(x)
    for t=3:n
        if x[t] < x[t-1] && x[t-1] >= x[t-2]
            push!(chps, t)
        end
    end
    chps
end
