# Syntax patterns
function prefix(io, call, argument1)
    print(io, call)
    print(io, ' ')
    print(io, argument1)
end

function parenthesize_prefix(io, call, argument1)
    print(io, call)
    print(io, ' ')
    print(io, '(')
    print(io, argument1)
    print(io, ')')
end

function postfix(io, call, argument1)
    print(io, argument1)
    print(io, ' ')
    print(io, call)
end

function tight_infix(io, call, argument1, argument2)
    print(io, argument1)
    print(io, call)
    print(io, argument2)
end

function infix(io, call, argument1, argument2, arguments...)
    print(io, argument1)
    print(io, ' ')
    print(io, call)
    print(io, ' ')
    infix(io, call, argument2, arguments...)
end

function infix(io, call, argument1)
    print(io, argument1)
end

function head_call_tail(io, call, argument1, arguments...)
    print(io, argument1)
    print(io, ' ')
    print(io, call)
    print(io, ' ')
    join(io, arguments, ", ")
end

function call_tail_head(io, call, argument1, arguments...)
    print(io, call)
    print(io, ' ')
    join(io, arguments, ", ")
    print(io, ' ')
    print(io, argument1)
end

function when_then_else(io, condition, action, arguments...)
    print(io, "WHEN ")
    print(io, condition)
    print(io, " THEN ")
    print(io, action)
    print(io, ' ')
    when_then_else(io, arguments...)
end

function when_then_else(io, action)
    print(io, "ELSE ")
    print(io, action)
end

function case(io, call, arguments...)
    print(io, call)
    print(io, ' ')
    when_then_else(io, arguments...)
    print(io, ' ')
    print(io, "END")
end

# Use the call to determine the syntax pattern
function show(io::IO, sql_expression::SQLExpression)
    call = sql_expression.call
    arguments = sql_expression.arguments

    if call === :.
        tight_infix(io, call, arguments...)
    elseif call === :(=)
        infix(io, call, arguments...)
    elseif call === :<
        infix(io, call, arguments...)
    elseif call === Symbol("<>")
        infix(io, call, arguments...)
    elseif call === Symbol("||")
        infix(io, call, arguments...)
    elseif call === :*
        infix(io, call, arguments...)
    elseif call === :/
        infix(io, call, arguments...)
    elseif call === :+
        infix(io, call, arguments...)
    elseif call === :-
        infix(io, call, arguments...)
    elseif call === :%
        infix(io, call, arguments...)
    elseif call === :||
        infix(io, call, arguments...)
    elseif call === :AND
        infix(io, call, arguments...)
    elseif call === :AS
        infix(io, call, arguments...)
    elseif call === :CASE
        case(io, call, arguments...)
    elseif call === :DESC
        postfix(io, call, arguments...)
    elseif call === :FROM
        parenthesize_prefix(io, call, arguments...)
    elseif call === Symbol("GROUP BY")
        head_call_tail(io, call, arguments...)
    elseif call === :IN
        infix(io, call, arguments...)
    elseif call === Symbol("INNER JOIN")
        infix(io, call, arguments...)
    elseif call === Symbol("IS NOT DISTINCT FROM")
        infix(io, call, arguments...)
    elseif call === Symbol("IS NULL")
        postfix(io, call, arguments...)
    elseif call === :LIKE
        infix(io, call, arguments...)
    elseif call === :LIMIT
        infix(io, call, arguments...)
    elseif call === :OFFSET
        infix(io, call, arguments...)
    elseif call === :ON
        infix(io, call, arguments...)
    elseif call === :OR
        infix(io, call, arguments...)
    elseif call === Symbol("ORDER BY")
        head_call_tail(io, call, arguments...)
    elseif call === :NOT
        prefix(io, call, arguments...)
    elseif call === :SELECT
        call_tail_head(io, call, arguments...)
    elseif call === Symbol("SELECT DISTINCT")
        call_tail_head(io, call, arguments...)
    elseif call === :WHERE
        infix(io, call, arguments...)
    else
        print(io, call)
        print(io, '(')
        join(io, arguments, ", ")
        print(io, ')')
    end
end

has_select(something) = false
has_select(expression::SQLExpression) =
    expression.call in (:SELECT, Symbol("SELECT DISTINCT"))

seek_select(something) = false
seek_select(expression::SQLExpression) =
    has_select(expression) ||
    any(seek_select(clause) for clause in expression.arguments)

# Default to * if no columns are specifcied
function finalize(sql_expression::SQLExpression)
    if !seek_select(sql_expression)
        SQLExpression(:SELECT, sql_expression, :*)
    else
        sql_expression
    end
end

function finalize(something)
    something
end

"""
    get_sql(it)

Use `get_sql` if you would like to see the SQL code generated by an SQLite
query.

```jldoctest example
julia> using QuerySQLite

julia> database = Database(joinpath(pathof(QuerySQLite) |> dirname |> dirname, "test", "Chinook_Sqlite.sqlite"));

julia> get_sql(database.Track)
SELECT * FROM (Track)
```
"""
function get_sql(it)
    finalize(translate(it.code))
end
export get_sql
