// Copyright 2016 The Tulsi Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Cocoa
import TulsiGenerator

private func main() {
  // Parse the commandline parameters to see if the app should operate in headless mode or not.
  let commandlineParser = TulsiCommandlineParser()

  let consoleLogger = EventLogger(verboseLevel: commandlineParser.arguments.verboseLevel,
                                  logToFile: commandlineParser.arguments.logToFile)
  consoleLogger.startLogging()

  if !commandlineParser.commandlineSentinalFound {
    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
    exit(0)
  }

  let version = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
  LogMessage.postSyslog("Tulsi CLI: version \(version)")

  let queue = DispatchQueue(label: "com.google.Tulsi.xcodeProjectGenerator", attributes: [])
  queue.async {
    do {
      switch commandlineParser.mode {
      case .invalid:
        print("Missing mode parameter. Please see the help message.")
        exit(2)

      case .tulsiProjectCreator:
        let generator = HeadlessTulsiProjectCreator(arguments: commandlineParser.arguments)
        try generator.generate()

      case .xcodeProjectGenerator:
        let generator = HeadlessXcodeProjectGenerator(arguments: commandlineParser.arguments)
        try generator.generate()

      case .markAnnouncementRead:
        let readMarker = HeadlessAnnouncementReadMarker(arguments: commandlineParser.arguments)
        try readMarker.markRead()
      }
    } catch HeadlessModeError.invalidConfigPath(let reason) {
      print("Invalid \(TulsiCommandlineParser.ParamGeneratorConfigLong) param: \(reason)")
      exit(11)
    } catch HeadlessModeError.invalidConfigFileContents(let reason) {
      print("Failed to read the given generator config: \(reason)")
      exit(12)
    } catch HeadlessModeError.explicitOutputOptionRequired {
      print("The \(TulsiCommandlineParser.ParamOutputFolderLong) option is required for the selected config")
      exit(13)
    } catch HeadlessModeError.invalidBazelPath {
      print("The path to the bazel binary is invalid")
      exit(14)
    } catch HeadlessModeError.generationFailed {
      print("Xcode project generation failed")
      exit(15)
    } catch HeadlessModeError.invalidWorkspaceRootOverride {
      print("The parameter given as the workspace root path is not a valid directory")
      exit(16)
    } catch HeadlessModeError.invalidProjectFileContents(let reason) {
      print("Failed to read the given project: \(reason)")
      exit(20)
    } catch HeadlessModeError.missingBazelPath {
      print("The path to the bazel binary must be specified")
      exit(21)
    } catch HeadlessModeError.missingWORKSPACEFile(let path) {
      print("The workspace root at '\(path)' does not contain a Bazel WORKSPACE file.")
      exit(21)
    } catch HeadlessModeError.missingBuildTargets {
      print("At least one build target must be specified with the \(TulsiCommandlineParser.ParamBuildTargetLong) parameter.")
      exit(22)
    } catch HeadlessModeError.missingAnnouncementId {
      print(
        "An announcement ID must be specified after the "
          + "\(TulsiCommandlineParser.ParamMarkAnnouncementRead) parameter to mark an "
          + "announcement as read.")
      exit(22)
    } catch HeadlessModeError.invalidAnnouncementId {
      print(
        "The announcement ID given is invalid. It must match the ID of a currently active "
          + "announcement.")
      exit(22)
    } catch HeadlessModeError.invalidProjectBundleName {
      print("The parameter given to \(TulsiCommandlineParser.ParamCreateTulsiProj) is invalid. " +
                "It must be the name of the .tulsiproj bundle to be created, without any other " +
                "path elements.")
      exit(23)
    } catch HeadlessModeError.bazelTargetProcessingFailed {
      print("Failed to identify any valid build targets.")
      exit(24)
    } catch let e as NSError {
      print("An unexpected exception occurred: \(e.localizedDescription)")
      exit(126)
    } catch {
      print("An unexpected exception occurred")
      exit(127)
    }

    // Ideally this would go just after generator.generate() inside the do block, but doing so trips
    // up the coverage tool as exit is @noreturn. It is important that all catch blocks exit with
    // non-zero codes so that they do not reach this.
    exit(0)
  }

  dispatchMain()
}

// MARK: - Application entrypoint

main()
