
"""
    show(io::IO, process::QEDProcessDescription)

Pretty print an [`QEDProcessDescription`](@ref) (no newlines).

```jldoctest
julia> using MetagraphOptimization

julia> print(parse_process("ke->ke", QEDModel()))
QED Process: 'ke->ke'

julia> print(parse_process("kk->ep", QEDModel()))
QED Process: 'kk->ep'
```
"""
function show(io::IO, process::QEDProcessDescription)
    # types() gives the types in order (QED) instead of random like keys() would
    print(io, "QED Process: \'")
    for type in types(QEDModel())
        for _ in 1:get(process.inParticles, type, 0)
            print(io, String(type))
        end
    end
    print(io, "->")
    for type in types(QEDModel())
        for _ in 1:get(process.outParticles, type, 0)
            print(io, String(type))
        end
    end
    print(io, "'")
    return nothing
end


"""
    String(process::QEDProcessDescription)

Create a short string suitable as a filename or similar, describing the given process.

```jldoctest
julia> using MetagraphOptimization

julia> String(parse_process("ke->ke", QEDModel()))
qed_ke-ke

julia> print(parse_process("kk->ep", QEDModel()))
qed_kk-ep
```
"""
function String(process::QEDProcessDescription)
    # types() gives the types in order (QED) instead of random like keys() would
    str = "qed_"
    for type in types(QEDModel())
        for _ in 1:get(process.inParticles, type, 0)
            str = str * String(type)
        end
    end
    str = str * "-"
    for type in types(QEDModel())
        for _ in 1:get(process.outParticles, type, 0)
            str = str * String(type)
        end
    end
    return str
end

"""
    show(io::IO, processInput::QEDProcessInput)

Pretty print a [`QEDProcessInput`](@ref) (with newlines).
"""
function show(io::IO, processInput::QEDProcessInput)
    println(io, "Input for $(processInput.process):")
    if !isempty(processInput.inFerms)
        println(io, "  $(processInput.inFerms)")
    end
    if !isempty(processInput.outFerms)
        println(io, "  $(processInput.outFerms)")
    end
    if !isempty(processInput.inAntiferms)
        println(io, "  $(processInput.inAntiferms)")
    end
    if !isempty(processInput.outAntiferms)
        println(io, "  $(processInput.outAntiferms)")
    end
    if !isempty(processInput.inPhotons)
        println(io, "  $(processInput.inPhotons)")
    end
    if !isempty(processInput.outPhotons)
        println(io, "  $(processInput.outPhotons)")
    end
    return nothing
end

"""
    show(io::IO, particle::T) where {T <: QEDParticle}

Pretty print a [`QEDParticle`](@ref) (no newlines).
"""
function show(io::IO, particle::T) where {T <: QEDParticle}
    print(io, "$(String(typeof(particle))): $(particle.momentum)")
    return nothing
end

"""
    show(io::IO, particle::FeynmanParticle)

Pretty print a [`FeynmanParticle`](@ref) (no newlines).
"""
show(io::IO, p::FeynmanParticle) = print(io, "$(String(p.particle))_$(String(direction(p.particle)))_$(p.id)")

"""
    show(io::IO, particle::FeynmanVertex)

Pretty print a [`FeynmanVertex`](@ref) (no newlines).
"""
show(io::IO, v::FeynmanVertex) = print(io, "$(v.in1) + $(v.in2) -> $(v.out)")

"""
    show(io::IO, particle::FeynmanTie)

Pretty print a [`FeynmanTie`](@ref) (no newlines).
"""
show(io::IO, t::FeynmanTie) = print(io, "$(t.in1) -- $(t.in2)")

"""
    show(io::IO, particle::FeynmanDiagram)

Pretty print a [`FeynmanDiagram`](@ref) (with newlines).
"""
function show(io::IO, d::FeynmanDiagram)
    print(io, "Initial Particles: [")
    first = true
    for p in d.particles
        if first
            first = false
            print(io, "$p")
        else
            print(io, ", $p")
        end
    end
    print(io, "]\n")
    for l in eachindex(d.vertices)
        print(io, "  Virtuality Level $l Vertices: [")
        first = true
        for v in d.vertices[l]
            if first
                first = false
                print(io, "$v")
            else
                print(io, ", $v")
            end
        end
        print(io, "]\n")
    end
    return print(io, "  Tie: $(d.tie[])\n")
end
