# PYTHON_ARGCOMPLETE_OK
"""
Parse cmake help to generate certain input files

TODO(josh): `cmake --help-module-list` to get a list of builtin find-modules
that we can search for more commands.
TODO(josh): `cmake --help-commandplist` to get a list of builtin commands
that we can generate parsers for.
"""

from __future__ import print_function, unicode_literals

import argparse
import io
import logging
import os
import re
import subprocess
import sys

import jinja2

logger = logging.getLogger(__name__)


def get_abspath(relpath):
  return os.path.abspath(os.path.join(os.path.dirname(__file__), relpath))


def sub_callback(match):
  # NOTE(josh): it would be handly to use named groups in regular expressions,
  # but since we use | to join them together into a big regex... the names
  # will collide so we cannot
  return "(?P<{}>.*)".format(re.sub(r"\W", "_", match.group(1)))


GENERIC_LABEL = re.compile(r"<([^>]+)>")


def make_pattern(namestr):
  """
  Look for any generic labels within a cmake property or variable name
  (e.g. `<LANG>` in `<LANG>_CPPLINT`) and convert the pattern string to a
  regular expression pattern (e.g. `.*_CPPLINT`)
  """
  return GENERIC_LABEL.sub(sub_callback, namestr)


def strip_named_groups(pattern):
  return re.sub(r"\(?P<[\w_]>", "(", pattern)


def get_properties(args, jenv):

  proc = subprocess.Popen(
      [args.cmake, "--help-property-list"],
      stdout=subprocess.PIPE)

  with proc.stdout as infile:
    properties = [line.decode("utf-8").strip() for line in infile]
  patterns = [make_pattern(namestr) for namestr in properties]
  proc.wait()

  template = jenv.get_template("properties.jinja.py")
  content = template.render(patterns=patterns)

  if args.outfile == "-":
    args.outfile = os.dup(sys.stdout.fileno())
  with io.open(args.outfile, "w", encoding="utf-8") as outfile:
    outfile.write(content)
    outfile.write("\n")


def get_variables(args, jenv):

  proc = subprocess.Popen(
      [args.cmake, "--help-variable-list"],
      stdout=subprocess.PIPE)

  with proc.stdout as infile:
    variables = [line.decode("utf-8").strip() for line in infile]
  patterns = [make_pattern(namestr) for namestr in variables]
  proc.wait()

  template = jenv.get_template("variables.jinja.py")
  content = template.render(patterns=patterns)

  if args.outfile == "-":
    args.outfile = os.dup(sys.stdout.fileno())
  with io.open(args.outfile, "w", encoding="utf-8") as outfile:
    outfile.write(content)
    outfile.write("\n")


def get_command_list(args):
  """
  Get a list of all the builtin cmake commands (statement names).
  """
  proc = subprocess.Popen(
      [args.cmake, "--help-command-list"],
      stdout=subprocess.PIPE)

  with proc.stdout as infile:
    return [line.decode("utf-8").strip() for line in infile]


def get_command_help(args, command_name):
  """
  Get the help string for a specific command
  """
  proc = subprocess.Popen(
      [args.cmake, "--help-command", command_name],
      stdout=subprocess.PIPE)
  with proc.stdout as infile:
    return infile.read().decode("utf-8")


def get_usages(helpstr):
  """
  Parse the command help string and return a list of command usage strings.
  """
  usage = []
  active = False

  buf = ""
  lineiter = iter(helpstr.split("\n"))
  for line in lineiter:
    if active:
      if not line.strip():
        active = False
        usage.append(buf)
        buf = ""
      else:
        buf += line.rstrip() + "\n"
    elif line.strip() == "::":
      next(lineiter, None)
      active = True

  if buf:
    usage.append(buf)
  return usage


def cmd_get_usages(args, jenv):  # pylint: disable=W0613
  for command_name in get_command_list(args):
    helpstr = get_command_help(args, command_name)
    for usage in get_usages(helpstr):
      print(usage)


def cmd_print_deprecated(args, jenv):  # pylint: disable=W0613
  for command_name in get_command_list(args):
    helpstr = get_command_help(args, command_name)
    if "Deprecated" in helpstr:
      print(command_name)


def setup_argparse(parser):
  """Setup argument parser"""
  parser.add_argument(
      "--cmake", default="cmake",
      help="Cmake binary path or command. Default is `cmake`")
  parser.add_argument(
      "--outfile", default="-",
      help="output file, default is stdout")
  subparsers = parser.add_subparsers(dest="command")
  subparsers.add_parser("properties")
  subparsers.add_parser("variables")
  subparsers.add_parser("usages")
  subparsers.add_parser("deprecated")


def main():
  parser = argparse.ArgumentParser(description=__doc__)
  setup_argparse(parser)
  try:
    import argcomplete
    argcomplete.autocomplete(parser)
  except ImportError:
    pass
  args = parser.parse_args()

  thisdir = os.path.abspath(os.path.dirname(__file__))
  jenv = jinja2.Environment(
      loader=jinja2.FileSystemLoader(thisdir)
  )

  if args.command == "properties":
    get_properties(args, jenv)
  elif args.command == "variables":
    get_variables(args, jenv)
  elif args.command == "usages":
    cmd_get_usages(args, jenv)
  elif args.command == "deprecated":
    cmd_print_deprecated(args, jenv)
  else:
    logger.warning("Unknown command %s", args.command)


if __name__ == "__main__":
  logging.basicConfig(level=logging.INFO)
  main()