using Manifolds, Manopt, Test, ManifoldsBase

@testset "Record Options" begin
    # helper to get debug as string
    io = IOBuffer()
    M = ManifoldsBase.DefaultManifold(2)
    x = [4.0, 2.0]
    o = GradientDescentOptions(x, StopAfterIteration(20), ConstantStepsize(1.0))
    f = y -> distance(M, y, x) .^ 2
    ∇f = y -> -2 * log(M, y, x)
    p = GradientProblem(M, f, ∇f)
    a = RecordIteration()
    # constructors
    rO = RecordOptions(o, a)
    @test Manopt.dispatch_options_decorator(rO) === Val{true}()
    @test get_options(o) == o
    @test get_options(rO) == o
    @test_throws MethodError get_options(p)
    #
    @test get_initial_stepsize(p, rO) == 1.0
    @test get_stepsize(p, rO, 1) == 1.0
    @test get_last_stepsize(p, rO, 1) == 1.0
    #
    @test rO.recordDictionary[:All] == a
    @test RecordOptions(o, [a]).recordDictionary[:All].group[1] == a
    @test RecordOptions(o, Dict(:A => a)).recordDictionary[:A] == a
    @test isa(
        RecordOptions(o, [:Iteration]).recordDictionary[:All].group[1], RecordIteration
    )
    @test !has_record(o)
    @test_throws ErrorException get_record(o)
    @test get_options(o) == o
    @test !has_record(DebugOptions(o, []))
    @test has_record(rO)
    @test_throws ErrorException get_record(o)
    @test length(get_record(rO, :All)) == 0
    @test length(get_record(rO)) == 0
    @test length(get_record(DebugOptions(rO, []))) == 0
    @test_throws ErrorException get_record(RecordOptions(o, Dict{Symbol,RecordAction}()))
    @test_throws ErrorException get_record(o)
    @test get_record(rO) == Array{Int64,1}()
    # RecordIteration
    @test a(p, o, 0) == nothing # inactive
    @test a(p, o, 1) == [1]
    @test a(p, o, 2) == [1, 2]
    @test a(p, o, 9) == [1, 2, 9]
    @test a(p, o, -1) == []
    # RecordGroup
    @test length(RecordGroup().group) == 0
    b = RecordGroup([RecordIteration(), RecordIteration()])
    b(p, o, 1)
    b(p, o, 2)
    @test b.group[1].recorded_values == [1, 2]
    @test b.group[2].recorded_values == [1, 2]
    @test get_record(b) == [(1, 1), (2, 2)]
    #RecordEvery
    c = RecordEvery(a, 10, true)
    @test c(p, o, 0) == nothing
    @test c(p, o, 1) == nothing
    @test c(p, o, 10) == [10]
    @test c(p, o, 20) == [10, 20]
    @test c(p, o, -1) == []
    @test get_record(c) == []
    # RecordChange
    c = RecordChange()
    c(p, o, 1)
    @test c.recorded_values == [0.0] # no x0 -> assume x0 is the first iterate
    o.x = [3.0, 2.0]
    c(p, o, 2)
    @test c.recorded_values == [0.0, 1.0] # no x0 -> assume x0 is the first iterate
    c = RecordChange([4.0, 2.0])
    c(p, o, 1)
    @test c.recorded_values == [1.0] # no x0 -> assume x0 is the first iterate
    # RecordEntry
    o.x = x
    d = RecordEntry(x, :x)
    d(p, o, 1)
    @test d.recorded_values == [x]
    d2 = RecordEntry(typeof(x), :x)
    d2(p, o, 1)
    @test d2.recorded_values == [x]
    # RecordEntryChange
    o.x = x
    e = RecordEntryChange(:x, (p, o, x, y) -> distance(p.M, x, y))
    @test update_storage!(e.storage, o) == (:x,)
    e2 = RecordEntryChange(x, :x, (p, o, x, y) -> distance(p.M, x, y))
    @test e.field == e2.field
    e(p, o, 1)
    @test e.recorded_values == [0.0]
    o.x = [3.0, 2.0]
    e(p, o, 2)
    @test e.recorded_values == [0.0, 1.0]
    # RecordIterate
    o.x = x
    f = RecordIterate(x)
    @test_throws ErrorException RecordIterate()
    f(p, o, 1)
    @test f.recorded_values == [x]
    # RecordCost
    g = RecordCost()
    g(p, o, 1)
    @test g.recorded_values == [0.0]
    o.x = [3.0, 2.0]
    g(p, o, 2)
    @test g.recorded_values == [0.0, 1.0]
    #RecordFactory
    o.∇ = [0.0, 0.0]
    rf = RecordFactory(o, [:Cost, :∇])
    @test isa(rf[:All], RecordGroup)
    @test isa(rf[:All].group[1], RecordCost)
    @test isa(rf[:All].group[2], RecordEntry)
    @test isa(RecordFactory(o, [2])[:All], RecordEvery)
    @test rf[:All].group[2].field == :∇
    @test length(rf[:All].group) == 2
    @test all(isa.(
        RecordFactory(o, [:Cost, :Iteration, :Change, :Iterate])[:All].group,
        [RecordCost, RecordIteration, RecordChange, RecordIterate],
    ))
    @test RecordActionFactory(o, g) == g
end
