# Mel-Log Spectrum Approximation filter (MLSADF)

# MLSABaseFilter represents a base filter of the MLSADF.
mutable struct MLSABaseFilter <: Filter
    order::Int
    α::Float64
    delay::Vector{Float64}

    function MLSABaseFilter(order::Int, α::Float64)
        new(order, α, zeros(order+1))
    end
end

delay(f::Filter) = f.delay
allpass_alpha(f::MLSABaseFilter) = f.α

function filt!(f::MLSABaseFilter, x::Float64, coef::Vector{Float64})
    d = delay(f)
    α = allpass_alpha(f)

    d[1] = x
    d[2] = (1.0-α*α)*d[1] + α*d[2]

    result = 0.0
    for i=3:length(coef)
        @inbounds d[i] = d[i] + α*(d[i+1] - d[i-1])
        @inbounds result += d[i] * coef[i]
    end

    # special case
    if length(coef) == 2
        result += d[2] * coef[2]
    end

    # t <- t+1 in time
    for i=length(d):-1:3
        @inbounds d[i] = d[i-1]
    end

    result
end

# MLSACascadeFilter represents a cascade filter which contains MLSA base
# filters.
mutable struct MLSACascadeFilter <: Filter
    filters::Vector{MLSABaseFilter}
    padecoef::Vector{Float64}
    delay::Vector{Float64}

    function MLSACascadeFilter(order::Int, α::Float64, pade::Int)
        padecoef = Vector{Float64}(pade+1)

        if pade == 4
            padecoef = padecoef4th
        elseif pade == 5
            padecoef = padecoef5th
        else
            error("MLSADF: Order of pade approximation 4 or 5 is only supported.")
        end

        filters = Vector{MLSABaseFilter}(pade+1)
        for i=1:length(filters)
            filters[i] = MLSABaseFilter(order, α)
        end

        new(filters, padecoef, zeros(pade+1))
    end
end

allpass_alpha(f::MLSACascadeFilter) = allpass_alpha(f.filters[1])
padecoef(f::MLSACascadeFilter) = f.padecoef

function filt!(f::MLSACascadeFilter, x::Float64, coef::Vector{Float64})
    d = delay(f)
    pade = padecoef(f)
    result, feedback = 0.0, 0.0

    for i=length(pade):-1:2
        @inbounds d[i] = filt!(f.filters[i], d[i-1], coef)
        @inbounds val = d[i] * pade[i]
        if iseven(i)
            feedback += val
        else
            feedback -= val
        end
        result += val
    end

    d[1] = feedback + x
    result += d[1]
    result
end

mutable struct MLSADF <: MelGeneralizedCepstrumSynthesisFilter
    filters::Vector{MLSACascadeFilter}

    function MLSADF(order::Int, α::Float64; pade::Int=5)
        filters = Vector{MLSACascadeFilter}(2)
        filters[1] = MLSACascadeFilter(2, α, pade)
        filters[2] = MLSACascadeFilter(order+1, α, pade)
        new(filters)
    end
end

first(f::MLSADF) = f.filters[1]
last(f::MLSADF) = f.filters[2]
allpass_alpha(f::MLSADF) = allpass_alpha(first(f))
glog_gamma(f::MLSADF) = 0.0

function filt!(f::MLSADF, x::Float64, coef::Vector{Float64})
    filt!(last(f), filt!(first(f), x, [0.0, coef[2]]), coef)
end

function to_filtcoef{T<:Union{MelCepstrum,LinearCepstrum}}(f::MLSADF,
                                                           mc::SpectralParamState{T})
    @assert allpass_alpha(paramdef(mc)) == allpass_alpha(f)
    rawdata(mgc2b(mc))
end
