### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file holds the code for the Eurasian Skylark (Alauda arvensis).
###

#XXX global variable/function
skylarkhabitat = @habitat((@landcover() == grass ||
                           (@landcover() == agriculture && @cropname() != "maize")) &&
                          @distancetoedge() > 50m)
#XXX this ought to check for distance to forest and builtup,
# but that's very expensive (see below)
# @distanceto(forest) > 5 && # at least 50m from forest edges
# @distanceto(builtup) > 5) # and from anthropogenic structures

"""
    Skylark

*Alauda arvensis* is a common and charismatic species of agricultural landscapes.
At the moment, this implementation is still in development.

**Sources:**
    - Bauer, H.-G., Bezzel, E., & Fiedler, W. (Eds.). (2012).
      Das Kompendium der Vögel Mitteleuropas: Ein umfassendes
      Handbuch zu Biologie, Gefährdung und Schutz (Einbändige
      Sonderausg. der 2., vollständig überarb. und erw. Aufl. 2005).
      AULA-Verlag
    - Glutz von Blotzheim, Urs N. (Ed.). (1985). Handbuch der
      Vögel Mitteleuropas. Bd. 10. Passeriformes (Teil 1)
      1. Alaudidae - Hirundidae. AULA-Verlag, Wiesbaden.
      ISBN 3-89104-019-9
"""
@species Skylark begin
    # species parameters
    const eggtime::Int64 = 11 # days from laying to hatching
    const nestlingtime::UnitRange{Int64} = 7:11 # days from hatching to leaving nest
    const fledglingtime::UnitRange{Int64} = 25:30 # days from hatching to independence

    const eggpredationmortality::Float64 = 0.03 # per-day egg mortality from predation
    const nestharvestmortality::Float64 = 0.9 # egg/nestling mortality after a harvest event (XXX guess)
    const nestlingpredationmortality::Float64 = 0.03 # per-day nestling mortality from predation
    const fledglingharvestmortality::Float64 = 0.5 # fledgling mortality after harvest
    const fledglingpredationmortality::Float64 = 0.01 # per-day fledgling mortality from predation
    const firstyearmortality::Float64 = 0.38 # total mortality in the first year after independence
    const migrationmortality::Float64 = 0.33 # chance of dying during the winter

    const nestingbegin::Tuple{Int64,Int64} = (April, 10) # begin nesting in the middle of April
    const nestbuildingtime::UnitRange{Int64} = 4:5 # 4-5 days needed to build a nest (doubled for first nest)
    const eggsperclutch::UnitRange{Int64} = 2:5 # eggs laid per clutch
    const breedingdelay::Int64 = 18 # days after hatching before starting a new brood
    const nestingend::Int64 = July # last month of nesting
    
    const habitats::Function = skylarkhabitat

    # individual variables
    daystonextphase::Int64 = 0 # days remaining until fledging or maturity
    migrationdates::Tuple = () # is defined by each individual in @create(Skylark)
    territory::Vector = [] # pixels that this skylark claims as its territory
    mate::Int64 = -1 # the agent ID of the mate (-1 if none)
    nest::Tuple = () # coordinates of current nest
    nestcompletion::Int64 = 0 # days left until the nest is built
    clutch::Vector{Int64} = Vector{Int64}() # IDs of offspring in current clutch
end

"""
As an egg, simply check for mortality and hatching.
"""
@phase Skylark egg begin
    @kill(self.eggpredationmortality, "predation")
    @respond(harvesting, @kill(self.nestharvestmortality, "harvest"))
    if self.age == self.eggtime
        self.daystonextphase = @rand(self.nestlingtime)
        @setphase(nestling)
    end
end

"""
As a nestling, simply check for mortality and fledging.
"""
@phase Skylark nestling begin
    #TODO add feeding & growth
    @kill(self.nestlingpredationmortality, "predation")
    @respond(harvesting, @kill(self.nestharvestmortality, "harvest"))
    # if self.age == self.nestlingtime+self.eggtime
    #     @setphase(fledgling)
    # end
    if self.daystonextphase == 0
        self.daystonextphase = @rand(self.fledglingtime)
        @setphase(fledgling)
    else
        self.daystonextphase -= 1
    end
end

"""
As a fledgling, move around a little, but mainly wait for maturity and
check mortality.
"""
@phase Skylark fledgling begin
    #TODO add feeding & growth
    @kill(self.fledglingpredationmortality, "predation")
    @walk("random", 10m) #TODO add movement following the parents
    # if self.age == self.fledglingtime+self.eggtime
    #     @kill(self.firstyearmortality, "first year mortality") #XXX mechanistic?
    #     @setphase(nonbreeding)
    # end
    if self.daystonextphase == 0
        @kill(self.firstyearmortality, "first year mortality") #XXX mechanistic?
        @setphase(nonbreeding)
    else
        self.daystonextphase -= 1
    end
end

"""
As a non-breeding adult, move around with other individuals and check for migration.
"""
@phase Skylark nonbreeding begin
    # flocking behaviour - follow a random neighbour or move randomly
    #TODO add feeding and mortality, respect habitat when moving
    neighbours = @neighbours(100m) #XXX magic number
    isempty(neighbours) ?
        @walk("random", 50m) :
        @follow(@rand(neighbours), 20m)
    # check if the bird migrates
    leave, arrive = self.migrationdates
    month, day = monthday(model.date)
    migrate = (((month < arrive[1]) || (month == arrive[1] && day < arrive[2])) ||
               ((month > leave[1]) || (month == leave[1] && day >= leave[2])))
    if migrate #FIXME not all migrate?
        @kill(self.migrationmortality, "migration")
        returndate = Date(year(model.date)+1, arrive[1], arrive[2])
        @setphase(mating)
        @migrate(returndate)
    end
end

"""
Move around until a mate is found.
"""
@phase Skylark mating begin
    #TODO mortality and feeding
    #TODO territoriality
    # if we've found a mate, wait for nesting begin and then go to the next phase
    if self.mate != -1
        if !@isalive(self.mate)
            self.mate = -1
            return
        end
        month, day = monthday(model.date)
        nest = ((month == self.nestingbegin[1] && day >= self.nestingbegin[2]
                 && @chance(0.05)) || (month > self.nestingbegin[1])) #XXX why the chance?
        nest && @setphase(nestbuilding)
        return
    end
    # look for a mate among the neighbouring birds, or move randomly
    for n in @neighbours(500m) #XXX magic number
        if n.sex != self.sex && n.phase == mating && n.mate == -1
            self.mate = n.id
            n.mate = self.id
            @debug "$(animalid(self)) and $(animalid(n)) have mated."
            return
        end
    end
    #@debug("$(animalid(self)) didn't find a mate.")
    @walk("random", 100m) #XXX magic number
end

"""
Females select a location and build a nest. Males do nothing. (Sound familiar?)
"""
@phase Skylark nestbuilding begin
    #TODO mortality and feeding
    if !@isalive(self.mate)
        self.mate = -1
        @setphase(nonbreeding)
        return
    end
    if self.sex == female
        if isempty(self.nest)
            # try to find a nest in the neighbourhood, or move on
            nestlocation = @randompixel(100m, self.habitats) #XXX magic number
            if isnothing(nestlocation)
                @walk("random", 200m) #XXX magic number
            else
                # if we've found a location, start the clock on the building time
                # (building time doubles for the first nest of the year)
                self.nest = nestlocation
                self.nestcompletion = @rand(self.nestbuildingtime)
                month(model.date) == self.nestingbegin[1] && (self.nestcompletion *= 2)
                @debug "$(animalid(self)) is building a nest."
            end
        else
            # wait while nest is being built, then lay eggs and go to next phase
            if self.nestcompletion > 0
                self.nestcompletion -= 1
            else
                #XXX more accurately, a female lays one egg per day, not all at once
                @reproduce(@rand(self.eggsperclutch),self.mate)
                @setphase(breeding)
            end
        end
    else
        # males stay near the female
        mate = @animal(self.mate)
        @follow(mate, 50m)
        mate.phase == breeding && @setphase(breeding)
    end
end

"""
Do lots of foraging (not yet implemented).
"""
@phase Skylark breeding begin
    #TODO forage (move inside the territory)
    for offspring in self.clutch
        # check if offspring are still alive and juvenile, else remove from clutch
        if !@isalive(offspring) || @animal(offspring).phase == nonbreeding
            filter!(x -> x != offspring, self.clutch)
        end
    end
    # if all young have fledged, move to nonbreeding (if it's July) or breed again
    if isempty(self.clutch)
        self.nest = ()
        if month(model.date) >= self.nestingend
            self.territory = []
            @setphase(nonbreeding)
        else
            @setphase(nestbuilding)
        end
    end
end

"""
    migrationdates(skylark, model)

Select the dates on which this skylark will leave for / return from its migration,
based on observed migration patterns.
"""
function migrationdates(skylark::Animal, model::SimulationModel)
    #TODO this ought to be temperature-dependent and dynamic
    minleave = skylark.sex == female ? (September, 15) : (October, 1)
    minarrive = skylark.sex == male ? (February, 15) : (March, 1)
    deltaleave = @rand(0:45) #XXX ought to be normally distributed
    deltaarrive = @rand(0:15) #XXX ought to be normally distributed
    leave = monthday(Date(2001, minleave[1], minleave[2]) + Day(deltaleave))
    arrive = monthday(Date(2001, minarrive[1], minarrive[2]) + Day(deltaarrive))
    (leave, arrive)
end

"""
Initialise a skylark individual. Selects migration dates and checks if the bird
should currently be on migration. Also sets other individual-specific variables.
"""
@create Skylark begin
    @debug "Added $(animalid(self)) at $(self.pos)"
    # calculate migration dates for this individual
    self.migrationdates = migrationdates(self, model)
    leave, arrive = self.migrationdates
    month, day = monthday(model.date)
    migrate = (((month < arrive[1]) || (month == arrive[1] && day < arrive[2])) ||
               ((month > leave[1]) || (month == leave[1] && day >= leave[2])))
    if migrate
        returndate = Date(year(model.date), arrive[1], arrive[2])
        model.date != @param(core.startdate) && (returndate += Year(1))
        @migrate(returndate)
    end
    #TODO other stuff?
end

@populate Skylark begin
    habitat = skylarkhabitat
    initphase = mating
    birthphase = egg
    indarea = 3ha
    pairs = true
end