"""
    ComtradeCfg

`ComtradeCfg` holds information from a COMTRADE "cfg" configuration file. Fields
include:

* `station_name::String` -- Identifier for the substation name.
* `rec_dev_id::String` -- Identifier for the recording device.
* `rev_year::Int` -- File format revision, normally 1991, 1999, or 2013.
* `tt::Int` -- Total number of channels.
* `nA::Int` -- Number of analog channels.
* `nD::Int` -- Number of digital channels.
* `A::DataFrame` -- Table of analog channel information, one row per channel.
* `D::DataFrame` -- Table of digital channel information, one row per channel.
* `lf::Float64` -- Nominal line frequency in hertz.
* `nrates::Int` -- Number of sample rates.
* `samp::Vector{Float64}` -- Sample rate in hertz.
* `endsamp::Vector{Int}` -- Numper of sample points at each sampling rate.
* `npts::Int` -- Number of sample points.
* `time` -- Time stamp of the first data point.
* `triggertime` -- Time stamp of the trigger time.
* `ft::ASCIIString` -- File type, options include "ASCII" or "BINARY".
* `timemult::Float64` -- Multiplication factor for the timestamp.
* `time_code::ASCIIString` -- Time difference between local time and UTC, including daylight savings (2013).
* `local_code::ASCIIString` -- Time difference between local time and UTC when time stamps are in UTC (2013).
* `tmq_code::ASCIIString` -- Time quality as defined in IEEE C37.118. F=not reliable, 4=time within 1 μs, 0=clock locked in (2013).
* `leapsec::Int` -- Leap second indicator, 0=no adjustment, 1=a leap second was added, 2=a leap second was subtracted, 3=no capability to address (2013).

`A` includes the following columns:

* `:An::Int` -- Analog channel index.
* `:ch_id::String` -- Channel identifier.
* `:ph::String` -- Channel phase identifier.
* `:ccbm::String` -- Circuit component.
* `:uu::String` -- Units.
* `:a::Float64` -- Channel multiplier.
* `:b::Float64` -- Channel offset adder.
* `:skew::Float64` -- Time skew in microseconds from the start of the sample period.
* `:min::Float64` -- Minimum data value.
* `:max::Float64` -- Maximum data value.
* `:primary::Float64` -- Voltage or current transformer ratio primary factor.
* `:secondary::Float64` -- Voltage or current transformer ratio secondary factor.
* `:PS::String` -- "P" means `ax+b` is a primary value; "S" means a secondary value.

`D` includes the following columns:

* `:Dn::Int` -- Digital channel index.
* `:ch_id::String` -- Channel identifier.
* `:ph::String` -- Channel phase identifier.
* `:ccbm::String` -- Circuit component.
* `:y::Int` -- Normal state, 0 or 1.

Note that the `:PS` column of `A` is not used to adjust the data in `read_comtrade`.
Likewise, `:skew` is not used to adjust the time stamp.

Some of the columns in `A` and `D` may be missing in older file revisions.
"""

using CSV

mutable struct ComtradeCfg
    station_name::String
    rec_dev_id::String
    rev_year::Int
    tt::Int
    nA::Int
    nD::Int
    A::DataFrame
    D::DataFrame
    lf::Float64
    nrates::Int
    samp::Vector{Float64}
    endsamp::Vector{Int}
    npts::Int
    time
    triggertime
    ft::String
    timemult::Float64
    time_code::String
    local_code::String
    tmq_code::String
    leapsec::Int
end

ComtradeCfg() = ComtradeCfg("", "", 1991, 0, 0, 0,
                            DataFrame(),
                            DataFrame(Any[Int[] for i in 1:4], [:Dn, :ch_id, :ph, :ccbm]),
                            0.0, 1, [0.0], [0], 0,
                            0, 0, "", 1.0,
                            "", "", "", 0)
asint(x) = parse(Int, x)
asfloat(x) = parse(Float64, x)

function read_cfg(fn)
    x = ComtradeCfg()
    a = readlines(fn)
    sa = split(a[1], ',')
    x.station_name = sa[1]
    if length(sa) > 1
        x.rec_dev_id = sa[2]
    end

    if length(sa) > 2
        x.rev_year = asint(sa[3])
    end
    sa = split(a[2], ',')
    x.tt = asint(sa[1])
    x.nA = asint(replace(sa[2], "A"=> ""))
    x.nD = asint(replace(sa[3], "D"=>""))
    x.A = CSV.read(fn, delim = ',', header = false, skipto = 3,
                    limit = x.nA)
    rename!(x.A, [:An, :ch_id, :ph, :ccbm, :uu, :a, :b, :skew, :min, :max, :primary, :secondary, :PS][1:ncol(x.A)])
    if x.nD > 0
        x.D = CSV.read(fn, delim = ',', header = false, skipto = 3 + x.nA,
                        limit = x.nD)
        if x.rev_year > 1991
            rename!(x.D, [:Dn, :ch_id, :ph, :ccbm, :y])
        else
            rename!(x.D, [:Dn, :ch_id, :y])
        end
    end
    x.nrates  = asint(a[x.tt + 4])
    for i in 1:max(x.nrates, 1)
        sa = split(a[x.tt + 4 + i], ',')
        x.samp[i]    = asfloat(sa[1])
        x.endsamp[i] = asint(sa[2])
    end
    nrow = x.tt + max(x.nrates, 1)
    x.npts = sum(x.endsamp)
    try
        x.time        = DateTimeMicro(strip(a[nrow + 5]))
    catch
        x.time        = strip(a[nrow + 5])
    end
    try
        x.triggertime = DateTimeMicro(strip(a[nrow + 6]))
    catch
        x.triggertime = strip(a[nrow + 6])
    end
    x.ft     = uppercase(strip(a[nrow + 7]))
    if x.rev_year >= 1999
        x.timemult = asfloat(a[nrow + 8])
    end
    if x.rev_year >= 2013
        sa = split(a[nrow + 9], ',')
        x.time_code = sa[1]
        x.local_code = strip(sa[2])
        sa = split(a[nrow + 10], ',')
        x.tmq_code = sa[1]
        x.leapsec = asint(sa[2])
    end
    return x
end
