// Copyright (c) 2011 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/chromeos/cros/power_library.h"

#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "content/browser/browser_thread.h"
#include "third_party/cros/chromeos_resume.h"

namespace chromeos {

class PowerLibraryImpl : public PowerLibrary {
 public:
  PowerLibraryImpl()
      : power_status_connection_(NULL),
        resume_status_connection_(NULL),
        status_(chromeos::PowerStatus()) {
    if (CrosLibrary::Get()->EnsureLoaded()) {
      Init();
    }
  }

  ~PowerLibraryImpl() {
    if (power_status_connection_) {
      chromeos::DisconnectPowerStatus(power_status_connection_);
      power_status_connection_ = NULL;
    }
    if (resume_status_connection_) {
      chromeos::DisconnectResume(resume_status_connection_);
      resume_status_connection_ = NULL;
    }
  }

  void AddObserver(Observer* observer) {
    observers_.AddObserver(observer);
  }

  void RemoveObserver(Observer* observer) {
    observers_.RemoveObserver(observer);
  }

  bool line_power_on() const {
    return status_.line_power_on;
  }

  bool battery_is_present() const {
    return status_.battery_is_present;
  }

  bool battery_fully_charged() const {
    return status_.battery_state == chromeos::BATTERY_STATE_FULLY_CHARGED;
  }

  double battery_percentage() const {
    return status_.battery_percentage;
  }

  base::TimeDelta battery_time_to_empty() const {
    return base::TimeDelta::FromSeconds(status_.battery_time_to_empty);
  }

  base::TimeDelta battery_time_to_full() const {
    return base::TimeDelta::FromSeconds(status_.battery_time_to_full);
  }

  virtual void EnableScreenLock(bool enable) {
    if (!CrosLibrary::Get()->EnsureLoaded())
      return;

    // Make sure we run on FILE thread becuase chromeos::EnableScreenLock
    // would write power manager config file to disk.
    if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
      BrowserThread::PostTask(
          BrowserThread::FILE, FROM_HERE,
          NewRunnableMethod(this, &PowerLibraryImpl::EnableScreenLock, enable));
      return;
    }

    chromeos::EnableScreenLock(enable);
  }

  virtual void RequestRestart() {
    if (!CrosLibrary::Get()->EnsureLoaded())
      return;
    chromeos::RequestRestart();
  }

  virtual void RequestShutdown() {
    if (!CrosLibrary::Get()->EnsureLoaded())
      return;
    chromeos::RequestShutdown();
  }

 private:
  static void PowerStatusChangedHandler(void* object,
      const chromeos::PowerStatus& status) {
    PowerLibraryImpl* power = static_cast<PowerLibraryImpl*>(object);
    power->UpdatePowerStatus(status);
  }

  static void SystemResumedHandler(void* object) {
    PowerLibraryImpl* power = static_cast<PowerLibraryImpl*>(object);
    power->SystemResumed();
  }

  void Init() {
    power_status_connection_ = chromeos::MonitorPowerStatus(
        &PowerStatusChangedHandler, this);
    resume_status_connection_ =
        chromeos::MonitorResume(&SystemResumedHandler, this);
  }

  void UpdatePowerStatus(const chromeos::PowerStatus& status) {
    // Make sure we run on UI thread.
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          NewRunnableMethod(
              this, &PowerLibraryImpl::UpdatePowerStatus, status));
      return;
    }

    DVLOG(1) << "Power lpo=" << status.line_power_on
             << " sta=" << status.battery_state
             << " per=" << status.battery_percentage
             << " tte=" << status.battery_time_to_empty
             << " ttf=" << status.battery_time_to_full;
    status_ = status;
    FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(this));
  }

  void SystemResumed() {
    // Make sure we run on the UI thread.
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          NewRunnableMethod(this, &PowerLibraryImpl::SystemResumed));
      return;
    }

    FOR_EACH_OBSERVER(Observer, observers_, SystemResumed());
  }

  ObserverList<Observer> observers_;

  // A reference to the battery power api, to allow callbacks when the battery
  // status changes.
  chromeos::PowerStatusConnection power_status_connection_;

  // A reference to the resume alerts.
  chromeos::ResumeConnection resume_status_connection_;

  // The latest power status.
  chromeos::PowerStatus status_;

  DISALLOW_COPY_AND_ASSIGN(PowerLibraryImpl);
};

class PowerLibraryStubImpl : public PowerLibrary {
 public:
  PowerLibraryStubImpl() {}
  ~PowerLibraryStubImpl() {}
  void AddObserver(Observer* observer) {}
  void RemoveObserver(Observer* observer) {}
  bool line_power_on() const { return false; }
  bool battery_is_present() const { return true; }
  bool battery_fully_charged() const { return false; }
  double battery_percentage() const { return 50.0; }
  base::TimeDelta battery_time_to_empty() const {
    return base::TimeDelta::FromSeconds(10 * 60);
  }
  base::TimeDelta battery_time_to_full() const {
    return base::TimeDelta::FromSeconds(0);
  }
  virtual void EnableScreenLock(bool enable) {}
  virtual void RequestRestart() {}
  virtual void RequestShutdown() {}
};

// static
PowerLibrary* PowerLibrary::GetImpl(bool stub) {
  if (stub)
    return new PowerLibraryStubImpl();
  else
    return new PowerLibraryImpl();
}

}  // namespace chromeos

// Allows InvokeLater without adding refcounting. This class is a Singleton and
// won't be deleted until it's last InvokeLater is run.
DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::PowerLibraryImpl);