### Persephone - a socio-economic-ecological model of European agricultural landscapes.
###
### This file includes functions for configuring the model and reading in map files.
###

## Note: much of this code was adapted from the GeMM model by Leidinger et al.
## (https://github.com/CCTB-Ecomods/gemm/blob/master/src/input.jl)

##XXX Does it make sense to move the settings dict into the model object itself?
## (It could be integrated into the properties dict.) Advantage: we have less
## global state lying around, and serialising becomes easier (if we want to do
## that). Also, during testing we often need to change individual parameters,
## which is currently impossible. Disadvantage: immutability of parameters at
## runtime is intended, and some start-up functions may have problems if they need
## access to parameters before the model object has been created.
##TODO -> yes, changing this is probably sensible
let settings::Dict{String, Dict{String, Any}}
    """
        initsettings(configfile)

    Initialise the global model settings for this run.
    """
    global function initsettings(configfile::String)
        settings = getsettings(configfile)
    end
    
    """
        param(domainparam)

    Return a configuration parameter from the global settings.
    The argument should be in the form `"<domain>.<parameter>"`,
    for example `param("core.outdir")`. Possible values for
    <domain> are "core", "nature", "farm", or "crop". For a full
    list of parameters, see `src/parameters.toml`.
    """
    global function param(domainparam::String)
        domain, paramname = split(domainparam, ".")
        settings[domain][paramname]
    end

    """
        printparameters(io)

    Print all settings in TOML format to the given output stream.
    """
    global function printparameters(stream::IO=stdout)
        TOML.print(stream, settings)
    end
end

"""
    getsettings(configfile)

Combines all configuration options to produce a single settings dict.
Precedence: commandline parameters - user config file - default values
"""
function getsettings(configfile::String)
    # read in and merge configurations from the commandline, the default config file
    # and a user-supplied config file
    defaults = TOML.parsefile(configfile)
    commandline = parsecommandline()
    if haskey(commandline, "configfile") && isfile(commandline["configfile"])
        configs = TOML.parsefile(commandline["configfile"])
    else
        configs = nothing
    end
    settings = deepcopy(defaults)
    for domain in keys(defaults)
        for param in keys(defaults[domain])
            if param in keys(commandline)
                settings[domain][param] = commandline[param]
            elseif !isnothing(configs) && param in keys(configs[domain])
                settings[domain][param] = configs[domain][param]
            end
        end
    end
    # pre-process certain parameters
    if settings["core"]["seed"] == 0
        settings["core"]["seed"] = abs(rand(RandomDevice(), Int32))
    end
    defaultoutdir = defaults["core"]["outdir"]
    if settings["core"]["outdir"] == defaultoutdir
        outdir = defaultoutdir*"_"*string(Dates.today())*"_s"*string(settings["core"]["seed"])
        settings["core"]["outdir"] = outdir
    end
    settings
end

"""
    parsecommandline()

Certain software parameters can be set via the commandline.
"""
function parsecommandline()
    versionstring = """
            Persephone $(@project_version), commit $(read(`git rev-parse HEAD`, String)[1:8])
            © 2022 Daniel Vedder, Lea Kolb (MIT license)
            https://git.idiv.de/xo30xoqa/persephone
            """
    s = ArgParseSettings(add_version=true, version=versionstring)
    @add_arg_table! s begin
        "--configfile", "-c"
            help = "name of the configuration file"
            arg_type = String
            required = false
        "--seed", "-s"
            help = "inital random seed"
            arg_type = Int
        "--outdir", "-o"
            help = "location of the output directory"
            arg_type = String
            required = false
        "--loglevel", "-l"
            help = "verbosity: \"debug\", \"info\", or \"quiet\""
            arg_type = String
            required = false
    end
    args = parse_args(s)
    for a in keys(args)
        (args[a] == nothing) && delete!(args, a)
    end
    args
end