module ClockParser

export get_skew

function _parse_line(line::AbstractString)
    time, high, _ = split(line)
    parse(Float64, time), parse(Float64, high)
end

"""
    get_skew(file::T) where {T <: AbstractString} -> Union{Float64, Missing}

Get skew from clock result file `file`.
It returns `missing` for invalid files.
To see the detailed reason, increase log level to `DEBUG`.
Invalid cases are:

- first line has >0 counts => "Unexpected first line"
- no measurement has >500 counts => "Clock skew out of range"
"""
function get_skew(file::T) where {T <: AbstractString}
    # @debug "file: $(file)"
    lines = Iterators.Stateful(eachline(file))

    was_0_before = false
    # criteria is changed from QAQC for skew parsing
    let
        first_line = popfirst!(lines)
        time, high = _parse_line(first_line)
        if high == 0
            was_0_before = true
        end
    end

    time_and_counts = Iterators.map(_parse_line, lines)
    for (time, high) in time_and_counts
        if high == 0
            was_0_before = true
        elseif was_0_before && high >= 500
            return time
        end
    end
    @debug "Clock skew out of range (file: $(file))"
    return missing
end

"""
    search_oscillation(file::T) where {T <: AbstractString}

Search oscillation (two or more rise up) for clock measurement file.
"""
function count_riseup(file::T) where {T <: AbstractString}
    lines = Iterators.Stateful(eachline(file))

    rising_count = 0
    first_line = popfirst!(lines)
    is_high = let
        time, high = _parse_line(first_line)
        high >= 500
    end

    time_and_counts = Iterators.map(_parse_line, lines)
    for (time, high) in time_and_counts
        if !is_high && high >= 500
            is_high = true
            rising_count += 1
        elseif is_high && high < 500
            is_high = false
        end
    end

    return rising_count

    # lines = eachline(file)
    # time_and_counts = Iterators.map(_parse_line, lines)
    # is_high = Iterators.map(time_and_counts) do (time, counts)
    #     counts >= 500
    # end
    # edges = Iterators.map(accumulate((p, n) -> (n, !p[1] & n), is_high, init = (false, false))) do x
    #     _prev, edge = x
    #     edge
    # end
    #
    # return sum(edges)
end

"""
Return Tuple of
- skew (first time >500)
- rise up full (last 0 to first 1000)

If clock is abnormal (i.e., which returns `missing` when [`get_skew`](@ref) is applied),
this returns Tuple of 2 `missing`s.
"""
function get_skew_and_riseup(file::T) where {T <: AbstractString}
    lines = Iterators.Stateful(eachline(file))

    last_low_time = missing
    first_high_time = missing
    skew = missing
    is_raised = false

    let
        _time, high = _parse_line(popfirst!(lines))
        if high == 0
            last_low_time = time
        end
    end

    for line in lines
        time, high = _parse_line(line)
        if high == 0
            last_low_time = time
        elseif !ismissing(last_low_time) && !is_raised && high >= 500
            skew = time
            is_raised = true
        elseif !ismissing(skew) && high == 1000
            first_high_time = time
            break
        end
    end

    if first_high_time === missing
        @debug "Clock skew out of range"
        return (missing, missing)
    end

    return (skew, first_high_time - last_low_time)
end

end # module ClockParser
