# -*- coding: utf-8 -*-
#
# Copyright 2011 Google Inc. All Rights Reserved.
#
"""Tools for recording and reporting timeline of abstract events.

You can store any events provided that they can be stringified.
"""

__author__ = 'kbaclawski@google.com (Krystian Baclawski)'

import collections
import datetime
import time


class _EventRecord(object):
  """Internal class.  Attaches extra information to an event."""

  def __init__(self, event, time_started=None, time_elapsed=None):
    self._event = event
    self._time_started = time_started or time.time()
    self._time_elapsed = None

    if time_elapsed:
      self.time_elapsed = time_elapsed

  @property
  def event(self):
    return self._event

  @property
  def time_started(self):
    return self._time_started

  def _TimeElapsedGet(self):
    if self.has_finished:
      time_elapsed = self._time_elapsed
    else:
      time_elapsed = time.time() - self._time_started

    return datetime.timedelta(seconds=time_elapsed)

  def _TimeElapsedSet(self, time_elapsed):
    if isinstance(time_elapsed, datetime.timedelta):
      self._time_elapsed = time_elapsed.seconds
    else:
      self._time_elapsed = time_elapsed

  time_elapsed = property(_TimeElapsedGet, _TimeElapsedSet)

  @property
  def has_finished(self):
    return self._time_elapsed is not None

  def GetTimeStartedFormatted(self):
    return time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(self._time_started))

  def GetTimeElapsedRounded(self):
    return datetime.timedelta(seconds=int(self.time_elapsed.seconds))

  def Finish(self):
    if not self.has_finished:
      self._time_elapsed = time.time() - self._time_started


class _Transition(collections.namedtuple('_Transition', ('from_', 'to_'))):
  """Internal class.  Represents transition point between events / states."""

  def __str__(self):
    return '%s => %s' % (self.from_, self.to_)


class EventHistory(collections.Sequence):
  """Records events and provides human readable events timeline."""

  def __init__(self, records=None):
    self._records = records or []

  def __len__(self):
    return len(self._records)

  def __iter__(self):
    return iter(self._records)

  def __getitem__(self, index):
    return self._records[index]

  @property
  def last(self):
    if self._records:
      return self._records[-1]

  def AddEvent(self, event):
    if self.last:
      self.last.Finish()

    evrec = _EventRecord(event)
    self._records.append(evrec)
    return evrec

  def GetTotalTime(self):
    if self._records:
      total_time_elapsed = sum(evrec.time_elapsed.seconds
                               for evrec in self._records)

      return datetime.timedelta(seconds=int(total_time_elapsed))

  def GetTransitionEventHistory(self):
    records = []

    if self._records:
      for num, next_evrec in enumerate(self._records[1:], start=1):
        evrec = self._records[num - 1]

        records.append(_EventRecord(
            _Transition(evrec.event, next_evrec.event), evrec.time_started,
            evrec.time_elapsed))

      if not self.last.has_finished:
        records.append(_EventRecord(
            _Transition(self.last.event,
                        'NOW'), self.last.time_started, self.last.time_elapsed))

    return EventHistory(records)

  @staticmethod
  def _GetReport(history, report_name):
    report = [report_name]

    for num, evrec in enumerate(history, start=1):
      time_elapsed = str(evrec.GetTimeElapsedRounded())

      if not evrec.has_finished:
        time_elapsed.append(' (not finished)')

      report.append('%d) %s: %s: %s' % (num, evrec.GetTimeStartedFormatted(),
                                        evrec.event, time_elapsed))

    report.append('Total Time: %s' % history.GetTotalTime())

    return '\n'.join(report)

  def GetEventReport(self):
    return EventHistory._GetReport(self, 'Timeline of events:')

  def GetTransitionEventReport(self):
    return EventHistory._GetReport(self.GetTransitionEventHistory(),
                                   'Timeline of transition events:')