// Copyright (c) 2012 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.

#ifndef CHROME_BROWSER_POLICY_LOGGING_WORK_SCHEDULER_H_
#define CHROME_BROWSER_POLICY_LOGGING_WORK_SCHEDULER_H_

#include <queue>
#include <vector>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/policy/delayed_work_scheduler.h"

// Utilities for testing users of DelayedWorkScheduler. There are no
// thread-safety guarantees for the classes in this file. They expect to
// only be called from the UI thread and issue callbacks on that very same
// thread.
//
// Usage examples:
//
// Making CloudPolicyController and/or DeviceTokenFetcher run without real-time
// delays in tests:
//
//   DeviceTokenFetcher fetcher(..., new DummyDelayedWorkScheduler);
//
// Running CloudPolicyController and/or DeviceTokenFetcher in a simulated
// environment, in which the time of any of their actions can be recorded,
// but without having to wait for the real-time delays:
//
//   EventLogger logger;
//   DeviceTokenFetcher fetcher(..., new LoggingEventScheduler(&logger));
//   CloudPolicyController controller(..., new LoggingEventScheduler(&logger));
//
// Start the policy subsystem, and use logger.RegisterEvent() in case of
// any interesting events. The time of all these events will be recorded
// by |logger|. After that, the results can be extracted easily:
//
//   std::vector<int64> logged_events;
//   logger.Swap(&logged_events);
//
// Each element of |logged_events| corresponds to a logger event, and stores
// the virtual time when it was logged. Events are in ascending order.

namespace policy {

// Helper class for LoggingWorkScheduler. It essentially emulates a real
// message loop. All the submitted tasks are run with zero delay, but the
// order in which they would run with delays is preserved.
// All the task posting requests of the schedulers will be channeled through
// a common instance of EventLogger. This makes sure, that this instance can
// keep track of time in the simulation and record logged events with correct
// timestamps.
class EventLogger {
 public:
  EventLogger();
  ~EventLogger();

  // Post a task to be executed |delay| milliseconds from now. The task can be
  // cancelled later by calling Reset() on the callback.
  void PostDelayedWork(linked_ptr<base::Closure> callback, int64 delay);

 // Register a new event that happened now according to the internal clock.
  void RegisterEvent();

  // Swap out the internal list of events.
  void Swap(std::vector<int64>* events);

  // Counts the events in a sorted integer array that are >= |start| but
  // < |start| + |length|.
  static int CountEvents(const std::vector<int64>& events,
                         int64 start, int64 length);

 private:
  class Task;

  // Updates |current_time_| and triggers the next scheduled task. This method
  // is run repeatedly on the main message loop until there are scheduled
  // tasks.
  void Step();

  // Stores the list of scheduled tasks with their respective delays and
  // schedulers.
  std::priority_queue<Task> scheduled_tasks_;

  // Machinery to put a call to |Step| at the end of the message loop.
  bool step_pending_;
  base::WeakPtrFactory<EventLogger> weak_ptr_factory_;

  // Ascending list of observation-times of the logged events.
  std::vector<int64> events_;
  // The current time of the simulated system.
  int64 current_time_;
  // The total number of tasks scheduled so far.
  int64 task_counter_;

  DISALLOW_COPY_AND_ASSIGN(EventLogger);
};

// Issues delayed tasks with zero effective delay, but posts them through
// an EventLogger, to make it possible to log events and reconstruct their
// execution time.
class LoggingWorkScheduler : public DelayedWorkScheduler {
 public:
  // An EventLogger may be shared by more than one schedulers, therefore
  // no ownership is taken.
  explicit LoggingWorkScheduler(EventLogger* logger);
  virtual ~LoggingWorkScheduler();

  virtual void PostDelayedWork(const base::Closure& callback, int64 delay)
      OVERRIDE;
  virtual void CancelDelayedWork() OVERRIDE;

 private:
  EventLogger* logger_;
  linked_ptr<base::Closure> callback_;

  DISALLOW_COPY_AND_ASSIGN(LoggingWorkScheduler);
};

// This implementation of DelayedWorkScheduler always schedules the tasks
// with zero delay.
class DummyWorkScheduler : public DelayedWorkScheduler {
 public:
  DummyWorkScheduler();
  virtual ~DummyWorkScheduler();

  virtual void PostDelayedWork(const base::Closure& callback, int64 delay)
      OVERRIDE;

 private:
  DISALLOW_COPY_AND_ASSIGN(DummyWorkScheduler);
};

}  // namespace policy

#endif  // CHROME_BROWSER_POLICY_LOGGING_WORK_SCHEDULER_H_