//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation

/// Generates the extensions to the lint and format pipelines.
final class PipelineGenerator: FileGenerator {

  /// The rules collected by scanning the formatter source code.
  let ruleCollector: RuleCollector

  /// Creates a new pipeline generator.
  init(ruleCollector: RuleCollector) {
    self.ruleCollector = ruleCollector
  }

  func write(into handle: FileHandle) throws {
    handle.write(
      """
      //===----------------------------------------------------------------------===//
      //
      // This source file is part of the Swift.org open source project
      //
      // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
      // Licensed under Apache License v2.0 with Runtime Library Exception
      //
      // See https://swift.org/LICENSE.txt for license information
      // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
      //
      //===----------------------------------------------------------------------===//

      // This file is automatically generated with generate-swift-format. Do not edit!

      import SwiftSyntax

      /// A syntax visitor that delegates to individual rules for linting.
      ///
      /// This file will be extended with `visit` methods in Pipelines+Generated.swift.
      class LintPipeline: SyntaxVisitor {

        /// The formatter context.
        let context: Context

        /// Stores lint and format rule instances, indexed by the `ObjectIdentifier` of a rule's
        /// class type.
        var ruleCache = [ObjectIdentifier: Rule]()

        /// Rules present in this dictionary skip visiting children until they leave the
        /// syntax node stored as their value
        var shouldSkipChildren = [ObjectIdentifier: SyntaxProtocol]()

        /// Creates a new lint pipeline.
        init(context: Context) {
          self.context = context
          super.init(viewMode: .sourceAccurate)
        }

      """
    )

    for (nodeType, lintRules) in ruleCollector.syntaxNodeLinters.sorted(by: { $0.key < $1.key }) {
      handle.write(
        """

          override func visit(_ node: \(nodeType)) -> SyntaxVisitorContinueKind {

        """)

      for ruleName in lintRules.sorted() {
        handle.write(
          """
              visitIfEnabled(\(ruleName).visit, for: node)

          """)
      }

      handle.write(
        """
            return .visitChildren
          }

        """)

        handle.write(
          """
            override func visitPost(_ node: \(nodeType)) {
          """
        )
        for ruleName in lintRules.sorted() {
          handle.write(
            """
                onVisitPost(rule: \(ruleName).self, for: node)

            """)
        }
        handle.write(
          """
            }

          """
        )
    }

    handle.write(
      """
      }

      extension FormatPipeline {

        func rewrite(_ node: Syntax) -> Syntax {
          var node = node

      """
    )

    for ruleName in ruleCollector.allFormatters.map({ $0.typeName }).sorted() {
      handle.write(
        """
            node = \(ruleName)(context: context).rewrite(node)

        """)
    }

    handle.write(
      """
          return node
        }
      }

      """)
  }
}
