# More generic implementation.
# https://github.com/ChrisRackauckas/Lists.jl/blob/master/src/Lists.jl
#
abstract type AbstractData end
abstract type AbstractNode end
struct NullNode <: AbstractNode end
const null = NullNode()

mutable struct Data <: AbstractData
    t::Int
    v::Float64
end

mutable struct Node{P, N} <: AbstractNode
    prev:: P
    next:: N
    data:: AbstractData
    function Node{P,N}(p,n,d) where {P,N}
        new(p,n,d)
    end
end
# Node(d::AbstractData) = Node{AbstractNode, AbstractNode}(null, null, d)
Node(d::AbstractData) = Node{AbstractNode, AbstractNode}(null, null, d)
Node(p::AbstractNode, n::AbstractNode, d::AbstractData) = Node{AbstractNode, AbstractNode}(p,n,d)

mutable struct List{T<:AbstractNode}
    # https://github.com/JuliaLang/julia/issues/20877
    # https://docs.julialang.org/en/latest/manual/constructors.html#Case-Study:-Rational-1
    head::T
    tail::T
    # function List{T}() where T <: AbstractNode
    #     new(null, null)
    # end
    function List{T}(nditem::T) where T <: AbstractNode
        new(nditem, nditem)
    end
end
### Start outer constructors
List() = List{NullNode}()
List(nd::AbstractNode) = List{AbstractNode}(nd)
### End outer constructors

function add!(list::List, data::Data)
    node = Node{NullNode, AbstractNode}(null, list.head, data)
    list.head = node
    node.next.prev = node
    node.prev = null # --todo
    println("Added!")
end
# list = List(Node(Data(1,0.1)))
# add!(list, Data(1, 1.0))
# add!(list, Data(1, 1.0))
nd1=Node(Data(1,0.1))
nd2=Node(Data(2,0.2))
println(typeof(nd1.prev) <: NullNode)
nd1.prev = nd2
println(typeof(nd1.prev) <: NullNode)

## tests
# function test_node()
#     init_node = Node(Data(1, 0.1))
#     node1 = Node(init_node, null, Data(2, 0.1))
#     node2 = Node(node1, null, Data(3, 0.1))
#     node3 = Node(node2, null, Data(4, 0.1))
#     typeof(node2.prev) <: NullNode
#     typeof(node3.next) <: NullNode
# end
# test_node()
