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

#include "chrome/browser/sync/notifier/chrome_system_resources.h"

#include <cstdlib>
#include <cstring>
#include <string>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "chrome/browser/sync/notifier/invalidation_util.h"

namespace sync_notifier {

ChromeSystemResources::ChromeSystemResources(StateWriter* state_writer)
    : state_writer_(state_writer),
      created_on_loop_(MessageLoop::current()) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK(created_on_loop_);
  DCHECK(state_writer_);
}

ChromeSystemResources::~ChromeSystemResources() {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  StopScheduler();
}

invalidation::Time ChromeSystemResources::current_time() {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  return base::Time::Now();
}

void ChromeSystemResources::StartScheduler() {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  scoped_runnable_method_factory_.reset(
      new ScopedRunnableMethodFactory<ChromeSystemResources>(this));
}

void ChromeSystemResources::StopScheduler() {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  scoped_runnable_method_factory_.reset();
  STLDeleteElements(&posted_tasks_);
}

void ChromeSystemResources::ScheduleWithDelay(
    invalidation::TimeDelta delay,
    invalidation::Closure* task) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  Task* task_to_post = MakeTaskToPost(task);
  if (!task_to_post) {
    return;
  }
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE, task_to_post, delay.InMillisecondsRoundedUp());
}

void ChromeSystemResources::ScheduleImmediately(
    invalidation::Closure* task) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  Task* task_to_post = MakeTaskToPost(task);
  if (!task_to_post) {
    return;
  }
  MessageLoop::current()->PostTask(FROM_HERE, task_to_post);
}

// The listener thread is just our current thread (i.e., the
// notifications thread).
void ChromeSystemResources::ScheduleOnListenerThread(
    invalidation::Closure* task) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  ScheduleImmediately(task);
}

// 'Internal thread' means 'not the listener thread'.  Since the
// listener thread is the notifications thread, always return false.
bool ChromeSystemResources::IsRunningOnInternalThread() {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  return false;
}

void ChromeSystemResources::Log(
    LogLevel level, const char* file, int line,
    const char* format, ...) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  logging::LogSeverity log_severity = logging::LOG_INFO;
  switch (level) {
    case INFO_LEVEL:
      log_severity = logging::LOG_INFO;
      break;
    case WARNING_LEVEL:
      log_severity = logging::LOG_WARNING;
      break;
    case SEVERE_LEVEL:
      log_severity = logging::LOG_ERROR;
      break;
  }
  // We treat LOG(INFO) as VLOG(1).
  if ((log_severity >= logging::GetMinLogLevel()) &&
      ((log_severity != logging::LOG_INFO) ||
       (1 <= logging::GetVlogLevelHelper(file, ::strlen(file))))) {
    va_list ap;
    va_start(ap, format);
    std::string result;
    base::StringAppendV(&result, format, ap);
    logging::LogMessage(file, line, log_severity).stream() << result;
    va_end(ap);
  }
}

void ChromeSystemResources::RunAndDeleteStorageCallback(
    invalidation::StorageCallback* callback) {
  callback->Run(true);
  delete callback;
}

void ChromeSystemResources::WriteState(
    const invalidation::string& state,
    invalidation::StorageCallback* callback) {
  CHECK(state_writer_);
  state_writer_->WriteState(state);
  // According to the cache invalidation API folks, we can do this as
  // long as we make sure to clear the persistent state that we start
  // up the cache invalidation client with.  However, we musn't do it
  // right away, as we may be called under a lock that the callback
  // uses.
  ScheduleImmediately(
      invalidation::NewPermanentCallback(
          this, &ChromeSystemResources::RunAndDeleteStorageCallback,
          callback));
}

Task* ChromeSystemResources::MakeTaskToPost(
    invalidation::Closure* task) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  DCHECK(invalidation::IsCallbackRepeatable(task));
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  if (!scoped_runnable_method_factory_.get()) {
    delete task;
    return NULL;
  }
  posted_tasks_.insert(task);
  Task* task_to_post =
      scoped_runnable_method_factory_->NewRunnableMethod(
          &ChromeSystemResources::RunPostedTask, task);
  return task_to_post;
}

void ChromeSystemResources::RunPostedTask(invalidation::Closure* task) {
  DCHECK(non_thread_safe_.CalledOnValidThread());
  CHECK_EQ(created_on_loop_, MessageLoop::current());
  RunAndDeleteClosure(task);
  posted_tasks_.erase(task);
}

}  // namespace sync_notifier