module TestConvert
using Compat
using Compat.Test
using CategoricalArrays
using CategoricalArrays: DefaultRefType, level, reftype, leveltype, catvalue, iscatvalue, CatValue

@testset "convert() for CategoricalPool{Int, DefaultRefType} and values" begin
    pool = CategoricalPool([1, 2, 3])
    @test convert(CategoricalPool{Int, DefaultRefType}, pool) === pool
    @test convert(CategoricalPool{Int}, pool) === pool
    @test convert(CategoricalPool, pool) === pool
    convert(CategoricalPool{Float64, UInt8}, pool)
    convert(CategoricalPool{Float64}, pool)
    convert(CategoricalPool, pool)

    v1 = catvalue(1, pool)
    v2 = catvalue(2, pool)
    v3 = catvalue(3, pool)
    @test iscatvalue(v1)
    @test iscatvalue(typeof(v1))
    @test eltype(v1) === Any
    @test eltype(typeof(v1)) === Any
    @test leveltype(v1) === Int
    @test leveltype(typeof(v1)) === Int
    @test reftype(v1) === DefaultRefType
    @test reftype(typeof(v1)) === DefaultRefType
    @test v1 isa CategoricalArrays.CategoricalValue{Int, DefaultRefType}

    @test convert(Int, v1) === 1
    @test convert(Int, v2) === 2
    @test convert(Int, v3) === 3

    @test convert(Int32, v1) === Int32(1)
    @test convert(Int32, v2) === Int32(2)
    @test convert(Int32, v3) === Int32(3)

    @test convert(UInt8, v1) === 0x01
    @test convert(UInt8, v2) === 0x02
    @test convert(UInt8, v3) === 0x03

    @test convert(CategoricalValue, v1) === v1
    @test convert(CategoricalValue{Int}, v1) === v1
    @test convert(CategoricalValue{Int, DefaultRefType}, v1) === v1

    @test convert(Any, v1) === v1
    @test convert(Any, v2) === v2
    @test convert(Any, v3) === v3

    for T in (typeof(v1), CatValue, CategoricalValue), U in (Missing, Nothing)
        @test convert(Union{T, U}, v1) === v1
        @test convert(Union{T, U}, v2) === v2
        @test convert(Union{T, U}, v3) === v3
    end

    @test get(v1) === 1
    @test get(v2) === 2
    @test get(v3) === 3

    @test promote(1, v1) === (1, 1)
    @test promote(1.0, v1) === (1.0, 1.0)
    @test promote(0x1, v1) === (1, 1)
end

@testset "convert() for CategoricalPool{String, DefaultRefType} and values" begin
    pool = CategoricalPool(["a", "b", "c"])
    @test convert(CategoricalPool{String, DefaultRefType}, pool) === pool
    @test convert(CategoricalPool{String}, pool) === pool
    @test convert(CategoricalPool, pool) === pool
    convert(CategoricalPool{String, UInt8}, pool)

    v1 = catvalue(1, pool)
    v2 = catvalue(2, pool)
    v3 = catvalue(3, pool)
    @test iscatvalue(v1)
    @test iscatvalue(typeof(v1))
    @test eltype(v1) === Char
    @test eltype(typeof(v1)) === Char
    @test leveltype(v1) === String
    @test leveltype(typeof(v1)) === String
    @test reftype(v1) === DefaultRefType
    @test reftype(typeof(v1)) === DefaultRefType
    @test v1 isa CategoricalArrays.CategoricalString{DefaultRefType}

    @test convert(String, v1) == "a"
    @test convert(String, v2) == "b"
    @test convert(String, v3) == "c"

    @test convert(AbstractString, v1) == "a"
    @test convert(AbstractString, v2) == "b"
    @test convert(AbstractString, v3) == "c"

    @test convert(CategoricalString, v1) === v1
    @test convert(CategoricalString{DefaultRefType}, v1) === v1

    @test convert(Any, v1) === v1
    @test convert(Any, v2) === v2
    @test convert(Any, v3) === v3

    for T in (typeof(v1), CatValue, CategoricalString, CategoricalString{DefaultRefType}),
        U in (Missing, Nothing)
        @test convert(Union{T, U}, v1) === v1
        @test convert(Union{T, U}, v2) === v2
        @test convert(Union{T, U}, v3) === v3
    end

    @test get(v1) === "a"
    @test get(v2) === "b"
    @test get(v3) === "c"
end

@testset "promote_type" begin
    @test promote_type(CategoricalValue{Int}, CategoricalValue{Float64}) ===
        CategoricalValue{Float64}
    @test promote_type(CategoricalValue{Int, UInt8}, CategoricalValue{Float64, UInt32}) ===
        CategoricalValue{Float64, UInt32}
    @test promote_type(CategoricalValue{Int, UInt8}, CategoricalValue{Float64}) ===
        CategoricalValue{Float64}
    # Tests that return Any before Julia 1.3 are due to JuliaLang/julia#29348
    if VERSION >= v"1.3.0-DEV"
        @test promote_type(CategoricalValue{Int},
                           Union{CategoricalValue{Float64}, Missing}) ===
            Union{Missing, Float64}
        @test promote_type(CategoricalValue{Int, UInt8},
                           Union{CategoricalValue{Float64, UInt32}, Missing}) ===
            Union{CategoricalValue{Float64, UInt32}, Missing}
        @test promote_type(Union{CategoricalValue{Int}, Missing},
                           CategoricalValue{Float64}) ===
            Union{Missing, Float64}
        @test promote_type(Union{CategoricalValue{Int, UInt8}, Missing},
                           CategoricalValue{Float64, UInt32}) ===
            Union{CategoricalValue{Float64, UInt32}, Missing}
        @test promote_type(Union{CategoricalValue{Int}, Missing},
                           Union{CategoricalValue{Float64}, Missing}) ===
            Union{Missing, Float64}
    else
        @test promote_type(CategoricalValue{Int},
                           Union{CategoricalValue{Float64}, Missing}) ===
            Any
        @test promote_type(CategoricalValue{Int, UInt8},
                           Union{CategoricalValue{Float64, UInt32}, Missing}) ===
            Union{CategoricalValue{Float64, UInt32}, Missing}
        @test promote_type(Union{CategoricalValue{Int}, Missing},
                           CategoricalValue{Float64}) ===
            Any
        @test promote_type(Union{CategoricalValue{Int, UInt8}, Missing},
                        CategoricalValue{Float64, UInt32}) ===
            Union{CategoricalValue{Float64, UInt32}, Missing}
        @test promote_type(Union{CategoricalValue{Int}, Missing},
                        Union{CategoricalValue{Float64}, Missing}) ===
            Any
    end
    @test promote_type(Union{CategoricalValue{Int, UInt8}, Missing},
                       Union{CategoricalValue{Float64, UInt32}, Missing}) ===
        Union{CategoricalValue{Float64, UInt32}, Missing}

    @test promote_type(CategoricalValue, Missing) === Union{CategoricalValue, Missing}
    @test promote_type(CategoricalValue{Int}, Missing) === Union{CategoricalValue{Int}, Missing}
    @test promote_type(CategoricalValue{Int, UInt32}, Missing) ===
        Union{CategoricalValue{Int, UInt32}, Missing}
    @test promote_type(CategoricalValue{Int, UInt32}, Any) === Any

    @test promote_type(CategoricalString{UInt8}, CategoricalString{UInt32}) ===
        CategoricalString{UInt32}
    @test promote_type(CategoricalString, CategoricalString{UInt32}) ===
        CategoricalString
    @test promote_type(Union{CategoricalString{UInt8}, Missing}, CategoricalString{UInt32}) ===
        Union{CategoricalString{UInt32}, Missing}
    @test promote_type(Union{CategoricalString{UInt8}, Missing}, Union{CategoricalString{UInt32}, Missing}) ===
        Union{CategoricalString{UInt32}, Missing}

    @test promote_type(CategoricalString{UInt8}, CategoricalValue{Int, UInt32}) ===
        CategoricalValue{Any, UInt32}
    @test promote_type(Union{CategoricalString{UInt8}, Missing}, CategoricalValue{Int, UInt32}) ===
        Union{CategoricalValue{Any, UInt32}, Missing}
end

@testset "convert() preserves `ordered`" begin
    pool = CategoricalPool([1, 2, 3], true)
    @test convert(CategoricalPool{Float64, UInt8}, pool).ordered === true
end

@testset "convert() with Union{T, Nothing}" begin
    pool = CategoricalPool([nothing, 2, 3])
    v1 = catvalue(1, pool)
    v2 = catvalue(2, pool)
    @test convert(Union{Int, Nothing}, v1) === nothing
    @test convert(Union{Int, Nothing}, v2) === 2
    @test convert(Union{Float64, Nothing}, v2) === 2.0
end


end
