# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import argparse
import os.path
import sys

_SRC_DIR = os.path.abspath(os.path.join(
    os.path.dirname(__file__), '..', '..', '..'))
sys.path.append(os.path.join(_SRC_DIR, 'build', 'android'))
from pylib import constants

class Options(object):
  """Global options repository.

  ParseArgs must be called before use. See _ARGS for common members, these will
  be available as instance attributes (eg, OPTIONS.clear_cache).
  """
  # Tuples of (argument name, default value, help string).
  _ARGS = [ ('clear_cache', True,
             'clear browser cache before loading'),
            ('chrome_package_name', 'chrome',
             'build/android/pylib/constants package description'),
            ('devtools_hostname', 'localhost',
             'hostname for devtools websocket connection'),
            ('devtools_port', 9222,
             'port for devtools websocket connection'),
            ('local_binary', os.path.join(_SRC_DIR, 'out/Release/chrome'),
             'chrome binary for local runs'),
            ('local_noisy', False,
             'Enable local chrome console output'),
            ('local_profile_dir', None,
             'profile directory to use for local runs'),
            ('no_sandbox', False,
             'pass --no-sandbox to browser (local run only; see also '
             'https://chromium.googlesource.com/chromium/src/+/master/'
             'docs/linux_suid_sandbox_development.md)'),
            ('devices_file', _SRC_DIR + '/third_party/WebKit/Source/devtools'
             '/front_end/emulated_devices/module.json', 'File containing a'
             ' list of emulated devices characteristics.'),
            ('emulate_device', '', 'Name of the device to emulate. Must be '
             'present in --devices_file, or empty for no emulation.'),
            ('emulate_network', '', 'Type of network emulation. Empty for no'
             ' emulation.')
          ]


  def __init__(self):
    self._arg_set = set()
    self._parsed_args = None

  def AddGlobalArgument(self, arg_name, default, help_str):
    """Add a global argument.

    Args:
      arg_name: the name of the argument. This will be used as an optional --
        argument.
      default: the default value for the argument. The type of this default will
        be used as the type of the argument.
      help_str: the argument help string.
    """
    self._ARGS.append((arg_name, default, help_str))

  def ParseArgs(self, arg_list, description=None, extra=None):
    """Parse command line arguments.

    Args:
      arg_list: command line argument list.
      description: description to use in argument parser.
      extra: additional required arguments to add. These will be exposed as
        instance attributes. This is either a list of extra arguments, or a
        single string or tuple. If a tuple, the first item is the argument and
        the second a default, otherwise the argument is required. Arguments are
        used as in argparse, ie those beginning with -- are named, and those
        without a dash are positional. Don't use a single dash.
    """
    parser = self._MakeParser(description, extra)
    self._parsed_args = parser.parse_args(arg_list)

  def ExtractArgs(self, arg_list):
    """Extract arguments from arg_str.

    Args:
      arg_list: command line argument list. It will be changed so that arguments
        used by this options instance are removed.
    """
    parser = self._MakeParser()
    (self._parsed_args, unused) = parser.parse_known_args(arg_list)
    del arg_list[:]
    arg_list.extend(unused)

  def GetParentParser(self, group_name='Global'):
    """Returns a parser suitable for passing in as a parent to argparse.

    Args:
      group_name: A group name for the parser (see argparse's
        add_argument_group).

    Returns:
      An argparse parser instance.
    """
    return self._MakeParser(group=group_name)

  def SetParsedArgs(self, parsed_args):
    """Set parsed args. Used with GetParentParser.

    Args:
      parsed_args: the result of argparse.parse_args or similar.
    """
    self._parsed_args = parsed_args

  def _MakeParser(self, description=None, extra=None, group=None):
    add_help = True if group is None else False
    parser = argparse.ArgumentParser(
        description=description, add_help=add_help)
    container = parser if group is None else parser.add_argument_group(group)
    for arg, default, help_str in self._ARGS:
      # All global options are named.
      arg = '--' + arg
      self._AddArg(container, arg, default, help_str=help_str)
    if extra is not None:
      if type(extra) is not list:
        extra = [extra]
      for arg in extra:
        if type(arg) is tuple:
          argname, default = arg
          self._AddArg(container, argname, default)
        else:
          self._AddArg(container, arg, None, required=True)
    return parser

  def _AddArg(self, container, arg, default, required=False, help_str=None):
    assert not arg.startswith('-') or arg.startswith('--'), \
        "Single dash arguments aren't supported: %s" % arg
    arg_name = arg
    if arg.startswith('--'):
      arg_name = arg[2:]
    assert arg_name not in self._arg_set, \
      '%s extra arg is a duplicate' % arg_name
    self._arg_set.add(arg_name)

    kwargs = {}
    if required and arg.startswith('--'):
      kwargs['required'] = required
    if help_str is not None:
      kwargs['help'] = help_str
    if default is not None:
      if type(default) is bool:
        # If the default of a switch is true, setting the flag stores false.
        if default:
          kwargs['action'] = 'store_false'
        else:
          kwargs['action'] = 'store_true'
      else:
        kwargs['default'] = default
        kwargs['type'] = type(default)

    container.add_argument(arg, **kwargs)

  def __getattr__(self, name):
    if name in self._arg_set:
      assert self._parsed_args, 'Option requested before ParseArgs called'
      return getattr(self._parsed_args, name)
    raise AttributeError(name)

  def ChromePackage(self):
    return constants.PACKAGE_INFO[self.chrome_package_name]

OPTIONS = Options()
