
"""
    compute(t::FusedComputeTask, data)

Compute a [`FusedComputeTask`](@ref). This simply asserts false and should not be called. Fused Compute Tasks generate their expressions directly through the other tasks instead.
"""
function compute(t::FusedComputeTask, data)
    @assert false "This is not implemented and should never be called"
end

"""
    get_expression(t::FusedComputeTask, device::AbstractDevice, inExprs::Vector{String}, outExpr::String)

Generate code evaluating a [`FusedComputeTask`](@ref) on `inExprs`, providing the output on `outExpr`.
`inExprs` should be of the correct types and may be heterogeneous. `outExpr` will be of the type of the output of `T2` of t.
"""
function get_expression(t::FusedComputeTask, device::AbstractDevice, inExprs::Vector, outExpr)
    inExprs1 = Vector()
    for sym in t.t1_inputs
        push!(inExprs1, gen_access_expr(device, sym))
    end

    outExpr1 = gen_access_expr(device, t.t1_output)

    inExprs2 = Vector()
    for sym in t.t2_inputs
        push!(inExprs2, gen_access_expr(device, sym))
    end

    expr1 = get_expression(t.first_task, device, inExprs1, outExpr1)
    expr2 = get_expression(t.second_task, device, [inExprs2..., outExpr1], outExpr)

    full_expr = Expr(:block, expr1, expr2)

    return full_expr
end

"""
    get_expression(node::ComputeTaskNode)

Generate and return code for a given [`ComputeTaskNode`](@ref).
"""
function get_expression(node::ComputeTaskNode)
    @assert length(children(node)) <= children(task(node)) "Node $(node) has too many children for its task: node has $(length(node.children)) versus task has $(children(task(node)))\nNode's children: $(getfield.(node.children, :children))"
    @assert !ismissing(node.device) "Trying to get expression for an unscheduled ComputeTaskNode\nNode: $(node)"

    inExprs = Vector()
    for id in getfield.(children(node), :id)
        push!(inExprs, gen_access_expr(node.device, Symbol(to_var_name(id))))
    end
    outExpr = gen_access_expr(node.device, Symbol(to_var_name(node.id)))

    return get_expression(task(node), node.device, inExprs, outExpr)
end

"""
    get_expression(node::DataTaskNode)

Generate and return code for a given [`DataTaskNode`](@ref).
"""
function get_expression(node::DataTaskNode)
    @assert length(children(node)) == 1 "Trying to call get_expression on a data task node that has $(length(node.children)) children instead of 1"

    # TODO: dispatch to device implementations generating the copy commands

    child = children(node)[1]
    inExpr = eval(gen_access_expr(child.device, Symbol(to_var_name(child.id))))
    outExpr = eval(gen_access_expr(child.device, Symbol(to_var_name(node.id))))
    dataTransportExp = Meta.parse("$outExpr = $inExpr")

    return dataTransportExp
end

"""
    get_init_expression(node::DataTaskNode, device::AbstractDevice)

Generate and return code for the initial input reading expression for [`DataTaskNode`](@ref)s with 0 children, i.e., entry nodes.

See also: [`get_entry_nodes`](@ref)
"""
function get_init_expression(node::DataTaskNode, device::AbstractDevice)
    @assert isempty(children(node)) "Trying to call get_init_expression on a data task node that is not an entry node."

    inExpr = eval(gen_access_expr(device, Symbol("$(to_var_name(node.id))_in")))
    outExpr = eval(gen_access_expr(device, Symbol(to_var_name(node.id))))
    dataTransportExp = Meta.parse("$outExpr = $inExpr")

    return dataTransportExp
end
