# Copyright 2013 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 logging

logger = logging.getLogger(__name__)


class OmapThrottlingDetector(object):
  """Class to detect and track thermal throttling on an OMAP 4."""
  OMAP_TEMP_FILE = ('/sys/devices/platform/omap/omap_temp_sensor.0/'
                    'temperature')

  @staticmethod
  def IsSupported(device):
    return device.FileExists(OmapThrottlingDetector.OMAP_TEMP_FILE)

  def __init__(self, device):
    self._device = device

  @staticmethod
  def BecameThrottled(log_line):
    return 'omap_thermal_throttle' in log_line

  @staticmethod
  def BecameUnthrottled(log_line):
    return 'omap_thermal_unthrottle' in log_line

  @staticmethod
  def GetThrottlingTemperature(log_line):
    if 'throttle_delayed_work_fn' in log_line:
      return float([s for s in log_line.split() if s.isdigit()][0]) / 1000.0

  def GetCurrentTemperature(self):
    tempdata = self._device.ReadFile(OmapThrottlingDetector.OMAP_TEMP_FILE)
    return float(tempdata) / 1000.0


class ExynosThrottlingDetector(object):
  """Class to detect and track thermal throttling on an Exynos 5."""

  @staticmethod
  def IsSupported(device):
    return device.FileExists('/sys/bus/exynos5-core')

  def __init__(self, device):
    pass

  @staticmethod
  def BecameThrottled(log_line):
    return 'exynos_tmu: Throttling interrupt' in log_line

  @staticmethod
  def BecameUnthrottled(log_line):
    return 'exynos_thermal_unthrottle: not throttling' in log_line

  @staticmethod
  def GetThrottlingTemperature(_log_line):
    return None

  @staticmethod
  def GetCurrentTemperature():
    return None


class ThermalThrottle(object):
  """Class to detect and track thermal throttling.

  Usage:
    Wait for IsThrottled() to be False before running test
    After running test call HasBeenThrottled() to find out if the
    test run was affected by thermal throttling.
  """

  def __init__(self, device):
    self._device = device
    self._throttled = False
    self._detector = None
    # pylint: disable=redefined-variable-type
    if OmapThrottlingDetector.IsSupported(device):
      self._detector = OmapThrottlingDetector(device)
    elif ExynosThrottlingDetector.IsSupported(device):
      self._detector = ExynosThrottlingDetector(device)

  def HasBeenThrottled(self):
    """True if there has been any throttling since the last call to
       HasBeenThrottled or IsThrottled.
    """
    return self._ReadLog()

  def IsThrottled(self):
    """True if currently throttled."""
    self._ReadLog()
    return self._throttled

  def _ReadLog(self):
    if not self._detector:
      return False
    has_been_throttled = False
    serial_number = str(self._device)
    log = self._device.RunShellCommand(['dmesg', '-c'],
                                       large_output=True,
                                       check_return=True)
    degree_symbol = unichr(0x00B0)
    for line in log:
      if self._detector.BecameThrottled(line):
        if not self._throttled:
          logger.warning('>>> Device %s thermally throttled', serial_number)
        self._throttled = True
        has_been_throttled = True
      elif self._detector.BecameUnthrottled(line):
        if self._throttled:
          logger.warning('>>> Device %s thermally unthrottled', serial_number)
        self._throttled = False
        has_been_throttled = True
      temperature = self._detector.GetThrottlingTemperature(line)
      if temperature is not None:
        logger.info(u'Device %s thermally throttled at %3.1f%sC', serial_number,
                    temperature, degree_symbol)

    if logger.isEnabledFor(logging.DEBUG):
      # Print current temperature of CPU SoC.
      temperature = self._detector.GetCurrentTemperature()
      if temperature is not None:
        logger.debug(u'Current SoC temperature of %s = %3.1f%sC', serial_number,
                     temperature, degree_symbol)

      # Print temperature of battery, to give a system temperature
      dumpsys_log = self._device.RunShellCommand(['dumpsys', 'battery'],
                                                 check_return=True)
      for line in dumpsys_log:
        if 'temperature' in line:
          btemp = float([s for s in line.split() if s.isdigit()][0]) / 10.0
          logger.debug(u'Current battery temperature of %s = %3.1f%sC',
                       serial_number, btemp, degree_symbol)

    return has_been_throttled
