### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file is responsible for managing the crop growth modules.
###

#TODO write tests for input functions

"""
    GrowthPhase

ALMaSS crop growth curves are split into five phases, triggered by
seasonal dates or agricultural events.
"""
@enum GrowthPhase janfirst sow marchfirst harvest1 harvest2

"""
    CropCurveParams

The values in this struct define one crop growth curve.
"""
struct CropCurveParams
    #TODO add Unitful
    curveID::Int
    highnutrients::Bool
    GDD::Dict{GrowthPhase,Vector{Float64}}
    LAItotal::Dict{GrowthPhase,Vector{Float64}}
    LAIgreen::Dict{GrowthPhase,Vector{Float64}}
    height::Dict{GrowthPhase,Vector{Float64}}
end

"""
    CropType

The type struct for all crops. Currently follows the crop growth model as
implemented in ALMaSS.
"""
struct CropType
    #TODO make this into an abstract type and create subtypes for different
    # crop submodels (#70)
    name::String
    minsowdate::Union{Missing,Date}
    maxsowdate::Union{Missing,Date}
    minharvestdate::Union{Missing,Date}
    maxharvestdate::Union{Missing,Date}
    mingrowthtemp::Union{Missing,Float64}
    highnutrientgrowth::Union{Missing,CropCurveParams}
    lownutrientgrowth::Union{Missing,CropCurveParams}
    #issowable::Union{Function,Bool}
end

"""
    Base.tryparse(type, str)

Extend `tryparse` to allow parsing GrowthPhase values.
(Needed to read in the CSV parameter file.)
"""
function Base.tryparse(type::Type{GrowthPhase}, str::String)
    str == "janfirst" ? janfirst :
        str == "sow" ? sow :
        str == "marchfirst" ? marchfirst :
        str == "harvest1" ? harvest1 :
        str == "harvest2" ? harvest2 :
        nothing
end

"""
    buildgrowthcurve(data)

Convert a list of rows from the crop growth data into a CropCurveParams object.
"""
function buildgrowthcurve(data::Vector{CSV.Row})
    isempty(data) && return missing
    GDD = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(),
               marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(),
               harvest2=>Vector{Float64}())
    LAItotal = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(),
                    marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(),
                    harvest2=>Vector{Float64}())
    LAIgreen = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(),
                    marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(),
                    harvest2=>Vector{Float64}())
    height = Dict(janfirst=>Vector{Float64}(), sow=>Vector{Float64}(),
                  marchfirst=>Vector{Float64}(), harvest1=>Vector{Float64}(),
                  harvest2=>Vector{Float64}())
    for e in data        
        append!(GDD[e.growth_phase], e.GDD)
        append!(LAItotal[e.growth_phase], e.LAI_total)
        append!(LAIgreen[e.growth_phase], e.LAI_green)
        append!(height[e.growth_phase], e.height)
    end
    CropCurveParams(data[1].curve_id, data[1].nutrient_status=="high",
                    GDD, LAItotal, LAIgreen, height)
end

"""
    readcropparameters(generalcropfile, cropgrowthfile)

Parse a CSV file containing the required parameter values for each crop
(as produced from the original ALMaSS file by `convert_almass_data.py`).
"""
function readcropparameters(generalcropfile::String, growthfile::String)
    @debug "Reading crop parameters"
    cropdata = CSV.File(generalcropfile, missingstring="NA", dateformat="d U",
                        types=[String,Date,Date,Date,Date,Float64])
    growthdata = CSV.File(growthfile, missingstring="NA",
                          types=[Int,String,String,GrowthPhase,String,
                                 Float64,Float64,Float64,Float64])
    croptypes = Dict{String,CropType}()
    for crop in cropdata
        cropgrowthdata = growthdata |> filter(x -> !ismissing(x.crop_name) &&
                                              x.crop_name == crop.name)
        highnuts = buildgrowthcurve(cropgrowthdata |>
                                    filter(x -> x.nutrient_status=="high"))
        lownuts = buildgrowthcurve(cropgrowthdata |>
                                   filter(x -> x.nutrient_status=="low"))
        croptypes[crop.name] = CropType(crop.name, crop.minsowdate, crop.maxsowdate,
                                        crop.minharvestdate, crop.maxharvestdate,
                                        crop.mingrowthtemp, highnuts, lownuts)
    end
    croptypes
end