# Implements a command line interface against the backend.

import backend, parseopt, strutils

const
  USAGE = """nimcalculator - Nimrod cross platform calculator
  (beta version, only integer addition is supported!)

Usage:
  nimcalculator [options] [-a=value -b=value]
Options:
  -a=value    sets the integer value of the a parameter
  -b=value    sets the integer value of the b parameter
  -h, --help  shows this help

If no options are used, an interactive mode is entered.
"""

type
  TCommand = enum       # The possible types of operation
    cmdParams,          # Two valid parameters were provided
    cmdInteractive      # No parameters were provided, run interactive mode

  TParamConfig = object of TObject
    action: TCommand      # store the type of operation
    paramA, paramB: int   # possibly store the valid parameters


proc parseCmdLine(): TParamConfig =
  ## Parses the commandline.
  ##
  ## Returns a TParamConfig structure filled with the proper values or directly
  ## calls quit() with the appropriate error message.
  var
    hasA = false
    hasB = false
    p = initOptParser()
    key, val: TaintedString

  result.action = cmdInteractive # By default presume interactive mode.
  try:
    while true:
      next p
      key = p.key
      val = p.val

      case p.kind
      of cmdArgument:
        stdout.write USAGE
        quit "Erroneous argument detected: " & key, 1
      of cmdLongOption, cmdShortOption:
        case key.normalize
        of "help", "h":
          stdout.write USAGE
          quit 0
        of "a":
          result.paramA = val.parseInt
          hasA = true
        of "b":
          result.paramB = val.parseInt
          hasB = true
        else:
          stdout.write USAGE
          quit "Unexpected option: " & key, 2
      of cmdEnd: break
  except EInvalidValue:
    stdout.write USAGE
    quit "Invalid value " & val &  " for parameter " & key, 3

  if hasA and hasB:
    result.action = cmdParams
  elif hasA or hasB:
    stdout.write USAGE
    quit "Error: provide both A and B to operate in param mode", 4


proc parseUserInput(question: string): int =
  ## Parses a line of user input, showing question to the user first.
  ##
  ## If the user input is an empty line quit() is called. Returns the value
  ## parsed as an integer.
  while true:
    echo question
    let input = stdin.readLine
    try:
      result = input.parseInt
      break
    except EInvalidValue:
      if input.len < 1: quit "Blank line detected, quitting.", 0
      echo "Sorry, `$1' doesn't seem to be a valid integer" % input

proc interactiveMode() =
  ## Asks the user for two integer values, adds them and exits.
  let
    paramA = parseUserInput("Enter the first parameter (blank to exit):")
    paramB = parseUserInput("Enter the second parameter (blank to exit):")
  echo "Calculating... $1 + $2 = $3" % [$paramA, $paramB,
    $backend.myAdd(paramA, paramB)]


when isMainModule:
  ## Main entry point.
  let opt = parseCmdLine()
  if cmdParams == opt.action:
    echo "Param mode: $1 + $2 = $3" % [$opt.paramA, $opt.paramB,
      $backend.myAdd(opt.paramA, opt.paramB)]
  else:
    echo "Entering interactive addition mode"
    interactiveMode()
