//
//  DebugVisitor.swift
//  Down
//
//  Created by John Nguyen on 09.04.19.
//

import Foundation

/// This visitor will generate the debug description of an entire abstract syntax tree,
/// indicating relationships between nodes with indentation.

public class DebugVisitor: Visitor {
    // MARK: - Properties

    private var depth = 0

    private var indent: String {
        String(repeating: "    ", count: depth)
    }

    // MARK: - Life cycle

    public init() {}

    // MARK: - Helpers

    private func report(_ node: Node) -> String {
        "\(indent)\(node is Document ? "" : "↳ ")\(String(reflecting: node))\n"
    }

    private func reportWithChildren(_ node: Node) -> String {
        let thisNode = report(node)
        depth += 1
        let children = visitChildren(of: node).joined()
        depth -= 1
        return "\(thisNode)\(children)"
    }

    // MARK: - Visitor

    public typealias Result = String

    public func visit(document node: Document) -> String {
        reportWithChildren(node)
    }

    public func visit(blockQuote node: BlockQuote) -> String {
        reportWithChildren(node)
    }

    public func visit(list node: List) -> String {
        reportWithChildren(node)
    }

    public func visit(item node: Item) -> String {
        reportWithChildren(node)
    }

    public func visit(codeBlock node: CodeBlock) -> String {
        reportWithChildren(node)
    }

    public func visit(htmlBlock node: HtmlBlock) -> String {
        reportWithChildren(node)
    }

    public func visit(customBlock node: CustomBlock) -> String {
        reportWithChildren(node)
    }

    public func visit(paragraph node: Paragraph) -> String {
        reportWithChildren(node)
    }

    public func visit(heading node: Heading) -> String {
        reportWithChildren(node)
    }

    public func visit(thematicBreak node: ThematicBreak) -> String {
        report(node)
    }

    public func visit(text node: Text) -> String {
        report(node)
    }

    public func visit(softBreak node: SoftBreak) -> String {
        report(node)
    }

    public func visit(lineBreak node: LineBreak) -> String {
        report(node)
    }

    public func visit(code node: Code) -> String {
        report(node)
    }

    public func visit(htmlInline node: HtmlInline) -> String {
        report(node)
    }

    public func visit(customInline node: CustomInline) -> String {
        report(node)
    }

    public func visit(emphasis node: Emphasis) -> String {
        reportWithChildren(node)
    }

    public func visit(strong node: Strong) -> String {
        reportWithChildren(node)
    }

    public func visit(link node: Link) -> String {
        reportWithChildren(node)
    }

    public func visit(image node: Image) -> String {
        reportWithChildren(node)
    }
}
