# This file is a part of BAT.jl, licensed under the MIT License (MIT).

bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::NoDensityTransform, ::AnyDensityLike) = DensityIdentityTransform()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::NoDensityTransform, ::AbstractPosteriorDensity) = DensityIdentityTransform()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::NoDensityTransform, ::DensityWithDiff{<:Any,<:AbstractPosteriorDensity}) = DensityIdentityTransform()

bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToUniform, ::AbstractPosteriorDensity) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToUniform, ::DensityWithDiff{<:Any,<:AbstractPosteriorDensity}) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToUniform, ::DistributionDensity) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToUniform, ::RenormalizedDensity{<:DistributionDensity}) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToUniform, ::StandardUniformDensity) = DensityIdentityTransform()

bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToGaussian, ::AbstractPosteriorDensity) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToGaussian, ::DensityWithDiff{<:Any,<:AbstractPosteriorDensity}) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToGaussian, ::DistributionDensity) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToGaussian, ::RenormalizedDensity{<:DistributionDensity}) = PriorSubstitution()
bat_default(::typeof(bat_transform), ::Val{:algorithm}, ::PriorToGaussian, ::StandardNormalDensity) = DensityIdentityTransform()

# ToDo: Add ToUnitBounded and ToUnbounded


@testset "UnitCell" begin
    cell = UnitCell(5, 3, 1, 110, 120, 80)

    @test lengths(cell) == [5.0, 3.0, 1.0]
    @test angles(cell) == [110.0, 120.0, 80.0]

    cell = UnitCell(2, 3, 4)

    @test lengths(cell) == [2.0, 3.0, 4.0]
    @test angles(cell) == [90.0, 90.0, 90.0]

    @test volume(cell) == 2.0 * 3.0 * 4.0

    set_lengths!(cell, 10, 20, 30)
    @test lengths(cell) == [10.0, 20.0, 30.0]

    remove_chemfiles_warning() do
        @test_throws ChemfilesError set_angles!(cell, 80, 89, 100)
    end

    expected = reshape(Float64[10, 0, 0,
                               0, 20, 0,
                               0, 0, 30], (3, 3))
    @test matrix(cell) ≈ expected atol=1e-10

    @test shape(cell) == Chemfiles.Orthorhombic

    set_shape!(cell, Chemfiles.Triclinic)
    @test shape(cell) == Chemfiles.Triclinic

    set_angles!(cell, 80, 89, 100)
    @test angles(cell) == [80.0, 89.0, 100.0]

    copy = deepcopy(cell)
    @test lengths(copy) == [10.0, 20.0, 30.0]

    set_lengths!(copy, 10, 10, 10)
    @test lengths(copy) == [10.0, 10.0, 10.0]
    @test lengths(cell) == [10.0, 20.0, 30.0]

    cell = UnitCell(10, 20, 30)
    @test wrap!(cell, [12.0, 22.0, -5.0]) == [2.0, 2.0, -5.0]
end


using Cambrian
import YAML

cfg = get_config("test.yaml")

@testset "Random fitness" begin
    e = Evolution{FloatIndividual}(cfg)

    evaluate(e::Evolution) = random_evaluate(e)
    populate(e::Evolution) = oneplus_populate(e)

    @test length(e.population) == cfg.n_population
    for i in e.population
        @test all(i.fitness .== -Inf)
    end

    evaluate(e)
    fits = [i.fitness[1] for i in e.population]
    evaluate(e)
    # random fitness, all values should change
    for i in eachindex(e.population)
        @test fits[i] != e.population[i].fitness[1]
    end

    fits = copy([i.fitness[:] for i in e.population])
    step!(e)
    # random fitness, all values should change
    for i in eachindex(e.population)
        @test fits[i] != e.population[i].fitness[1]
    end
end


## Parameter types used by Junet for multiple dispatch ##

"""
    DirParam

Abstract parent for parameter types encoding directedness.

In Junet, all data is stored in data structures that are agnostic of graphs'
directionality. The directionality is encoded in type parameters, for which
`DirParam` provides an abstract parent type.

The interpretation of those parameter types depends on each particular type that
uses them, but there are some commonalities:
  * `Forward` means interpreting data right as it's stored, e.g., look in the
    `forward` field to find info about edges originating from a node.
  * `Reverse` is the opposite of `Forward`.
  * `Both` means to look in both directions. Typically leads to processing two
    more data than with the previous ones.

For good presentation, they have two parents — `Directed` and `Undirected`,
by the type of graphs in which they are used.
"""
abstract type DirParam end
abstract type Directed <: DirParam end
abstract type Forward  <: Directed end
abstract type Reverse  <: Directed end
abstract type Undirected <: DirParam end
abstract type Both       <: Undirected end

dir_xor(::Type{D}, ::Type{R}) where {D<:Forward, R<:Forward} = Forward
dir_xor(::Type{D}, ::Type{R}) where {D<:Forward, R<:Reverse} = Reverse
dir_xor(::Type{D}, ::Type{R}) where {D<:Reverse, R<:Forward} = Reverse
dir_xor(::Type{D}, ::Type{R}) where {D<:Reverse, R<:Reverse} = Forward
dir_xor(::Type{D}, ::Type{R}) where {D<:DirParam, R<:DirParam} = Both

"""
    MultiParam

Similar to `DirParam`, this abstract type is for encoding whether graph may
have multiple edges. There are two child types: `Multi` amd `Simple`.
"""
abstract type MultiParam end
abstract type Multi  <: MultiParam end
abstract type Simple <: MultiParam end


include("axelrodnet.jl")

agentcount::Int64=100,
n_iter::Int64=1000,
socialbotfrac::Float64=0.00,
m0::Int64=5,
rndseed::Int64=1,
repcount::Int64=1,
completemixing::Bool=false

test = Axelrodnet.run!()
test_2 = Axelrodnet.run!(sqrt_agentcount=1000, n_iter=1000, repcount=3)
using CSV
fullmix = Axelrodnet.run!(completemixing = true)

print(test[1])
print(fullmix[1])

grid([3,3])

trunc(Int64, 5.5)

test2 = convert2weightedGraph(test[1], test[2])

using DataFrames

d = DataFrame(A = Int64[], B = Any[])

append!(
    d,
    DataFrame(
        A = [1, 2, 3],
        B = [3, "b", 3]
    )
)


if VERSION >= v"0.5.0-dev+7720"
    using Base.Test
else
    using BaseTestNext
    const Test = BaseTestNext
end
using JPLEphemeris
import Compat: ASCIIString

const AU = 0.149597870700000000e+09

const SPK_URL = Dict{Int, ASCIIString}(
    430 => "http://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de430.bsp",
    405 => "http://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp",
)
const TEST_URL = Dict{Int, ASCIIString}(
    430 => "ftp://ssd.jpl.nasa.gov/pub/eph/planets/test-data/430/testpo.430",
    405 => "ftp://ssd.jpl.nasa.gov/pub/eph/planets/test-data/testpo.405",
)

path = abspath(joinpath(dirname(@__FILE__), "..", "deps"))

for denum in (430, 405)
    if !isdir(path)
        mkdir(path)
    end
    if !isfile("$path/de$denum.bsp")
        download(SPK_URL[denum], "$path/de$denum.bsp")
    end
    if !isfile("$path/testpo.$denum")
        download(TEST_URL[denum], "$path/testpo.$denum")
    end
end

include("basic.jl")
include("kernels.jl")


@testset "Counters" begin
    @testset "SerialMem" begin
        seq = LongSequence{DNAAlphabet{2}}("ACTGAAGAGC")
        @test serial_mem(DNAMer{4}, NONCANONICAL)(seq) == [
            MerCount{DNAMer{4}}(mer"AAGA",1), MerCount{DNAMer{4}}(mer"ACTG",1),
            MerCount{DNAMer{4}}(mer"AGAG",1), MerCount{DNAMer{4}}(mer"CTGA",1),
            MerCount{DNAMer{4}}(mer"GAAG",1), MerCount{DNAMer{4}}(mer"GAGC",1),
            MerCount{DNAMer{4}}(mer"TGAA",1)
        ]
    end

end