# Copyright 2015 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.
"""This module wraps Android's fastboot tool.

This is a thin wrapper around the fastboot interface. Any additional complexity
should be delegated to a higher level (ex. FastbootUtils).
"""
# pylint: disable=unused-argument

from devil import devil_env
from devil.android import decorators
from devil.android import device_errors
from devil.utils import cmd_helper
from devil.utils import lazy

_DEFAULT_TIMEOUT = 30
_DEFAULT_RETRIES = 3
_FLASH_TIMEOUT = _DEFAULT_TIMEOUT * 10


class Fastboot(object):

  _fastboot_path = lazy.WeakConstant(lambda: devil_env.config.FetchPath(
      'fastboot'))

  def __init__(self,
               device_serial,
               default_timeout=_DEFAULT_TIMEOUT,
               default_retries=_DEFAULT_RETRIES):
    """Initializes the FastbootWrapper.

    Args:
      device_serial: The device serial number as a string.
    """
    if not device_serial:
      raise ValueError('A device serial must be specified')
    self._device_serial = str(device_serial)
    self._default_timeout = default_timeout
    self._default_retries = default_retries

  def __str__(self):
    return self._device_serial

  @classmethod
  def _RunFastbootCommand(cls, cmd):
    """Run a generic fastboot command.

    Args:
      cmd: Command to run. Must be list of args, the first one being the command

    Returns:
      output of command.

    Raises:
      TypeError: If cmd is not of type list.
    """
    if isinstance(cmd, list):
      cmd = [cls._fastboot_path.read()] + cmd
    else:
      raise TypeError('Command for _RunDeviceFastbootCommand must be a list.')
    # fastboot can't be trusted to keep non-error output out of stderr, so
    # capture stderr as part of stdout.
    status, output = cmd_helper.GetCmdStatusAndOutput(cmd, merge_stderr=True)
    if int(status) != 0:
      raise device_errors.FastbootCommandFailedError(cmd, output, status)
    return output

  def _RunDeviceFastbootCommand(self, cmd):
    """Run a fastboot command on the device associated with this object.

    Args:
      cmd: Command to run. Must be list of args, the first one being the command

    Returns:
      output of command.

    Raises:
      TypeError: If cmd is not of type list.
    """
    if isinstance(cmd, list):
      cmd = ['-s', self._device_serial] + cmd
    return self._RunFastbootCommand(cmd)

  @decorators.WithTimeoutAndRetriesDefaults(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
  def GetVar(self, variable, timeout=None, retries=None):
    args = ['getvar', variable]
    output = self._RunDeviceFastbootCommand(args)
    # getvar returns timing information on the last line of output, so only
    # parse the first line.
    output = output.splitlines()[0]
    # And the first line should match the format '$(var): $(value)'.
    if variable + ': ' not in output:
      raise device_errors.FastbootCommandFailedError(
          args, output, message="Unknown 'getvar' output format.")
    return output.split('%s: ' % variable)[1].strip()

  @decorators.WithTimeoutAndRetriesDefaults(_FLASH_TIMEOUT, 0)
  def Flash(self, partition, image, timeout=None, retries=None):
    """Flash partition with img.

    Args:
      partition: Partition to be flashed.
      image: location of image to flash with.
    """
    self._RunDeviceFastbootCommand(['flash', partition, image])

  @classmethod
  @decorators.WithTimeoutAndRetriesDefaults(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
  def Devices(cls, timeout=None, retries=None):
    """Outputs list of devices in fastboot mode.

    Returns:
      List of Fastboot objects, one for each device in fastboot.
    """
    output = cls._RunFastbootCommand(['devices'])
    return [Fastboot(line.split()[0]) for line in output.splitlines()]

  @decorators.WithTimeoutAndRetriesFromInstance()
  def RebootBootloader(self, timeout=None, retries=None):
    """Reboot from fastboot, into fastboot."""
    self._RunDeviceFastbootCommand(['reboot-bootloader'])

  @decorators.WithTimeoutAndRetriesDefaults(_FLASH_TIMEOUT, 0)
  def Reboot(self, timeout=None, retries=None):
    """Reboot from fastboot to normal usage"""
    self._RunDeviceFastbootCommand(['reboot'])

  @decorators.WithTimeoutAndRetriesFromInstance()
  def SetOemOffModeCharge(self, value, timeout=None, retries=None):
    """Sets off mode charging

    Args:
      value: boolean value to set off-mode-charging on or off.
    """
    self._RunDeviceFastbootCommand(['oem', 'off-mode-charge', str(int(value))])
