# Copyright 2017 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 optparse
import threading

import py_utils

from devil.android import device_utils
from systrace import trace_result
from systrace import tracing_agents
from py_trace_event import trace_time as trace_time_module

TRACE_FILE_PATH = \
    '/sdcard/Android/data/org.chromium.latency.walt/files/trace.txt'

CLOCK_DOMAIN_MARKER = '# clock_type=LINUX_CLOCK_MONOTONIC\n'


def try_create_agent(options):
  if options.is_walt_enabled:
    return WaltAgent()
  return None


class WaltConfig(tracing_agents.TracingConfig):
  def __init__(self, device_serial_number, is_walt_enabled):
    tracing_agents.TracingConfig.__init__(self)
    self.device_serial_number = device_serial_number
    self.is_walt_enabled = is_walt_enabled


def add_options(parser):
  options = optparse.OptionGroup(parser, 'WALT trace options')
  options.add_option('--walt', dest='is_walt_enabled', default=False,
                    action='store_true', help='Use the WALT tracing agent. '
                    'WALT is a device for measuring latency of physical '
                    'sensors on phones and computers. '
                    'See https://github.com/google/walt')
  return options


def get_config(options):
  return WaltConfig(options.device_serial_number, options.is_walt_enabled)


class WaltAgent(tracing_agents.TracingAgent):
  """
  This tracing agent requires the WALT app to be installed on the Android phone,
  and requires the WALT device to be attached to the phone. WALT is a device
  for measuring latency of physical sensors and outputs on phones and
  computers. For more information, visit https://github.com/google/walt
  """
  def __init__(self):
    super(WaltAgent, self).__init__()
    self._trace_contents = None
    self._config = None
    self._device_utils = None
    self._clock_sync_marker = None
    self._collection_thread = None

  def __repr__(self):
    return 'WaltAgent'

  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
  def StartAgentTracing(self, config, timeout=None):
    del timeout  # unused
    self._config = config
    self._device_utils = device_utils.DeviceUtils(
        self._config.device_serial_number)
    if self._device_utils.PathExists(TRACE_FILE_PATH):
      # clear old trace events so they are not included in the current trace
      self._device_utils.WriteFile(TRACE_FILE_PATH, '')
    return True

  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
  def StopAgentTracing(self, timeout=None):
    """Stops tracing and starts collecting results.

    To synchronously retrieve the results after calling this function,
    call GetResults().
    """
    del timeout  # unused
    self._collection_thread = threading.Thread(
        target=self._collect_trace_data)
    self._collection_thread.start()
    return True

  def _collect_trace_data(self):
    self._trace_contents = self._device_utils.ReadFile(TRACE_FILE_PATH)

  def SupportsExplicitClockSync(self):
    return True

  def RecordClockSyncMarker(self, sync_id, did_record_clock_sync_callback):
    cmd = 'cat /proc/timer_list | grep now'
    t1 = trace_time_module.Now()
    command_result = self._device_utils.RunShellCommand(cmd, shell=True)
    nsec = command_result[0].split()[2]
    self._clock_sync_marker = format_clock_sync_marker(sync_id, nsec)
    did_record_clock_sync_callback(t1, sync_id)

  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
  def GetResults(self, timeout=None):
    del timeout  # unused
    self._collection_thread.join()
    self._collection_thread = None
    return trace_result.TraceResult('waltTrace', self._get_trace_result())

  def _get_trace_result(self):
    result = '# tracer: \n' + CLOCK_DOMAIN_MARKER + self._trace_contents
    if self._clock_sync_marker is not None:
      result += self._clock_sync_marker
    return result


def format_clock_sync_marker(sync_id, nanosec_time):
  return ('<0>-0  (-----) [001] ...1  ' + str(float(nanosec_time) / 1e9)
          + ': tracing_mark_write: trace_event_clock_sync: name='
          + sync_id + '\n')
