// Copyright 2014 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/device/input_service_proxy.h"

#include "base/task_runner_util.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;
using device::InputServiceLinux;

typedef device::InputServiceLinux::InputDeviceInfo InputDeviceInfo;

namespace chromeos {

class InputServiceProxy::ServiceObserver : public InputServiceLinux::Observer {
 public:
  ServiceObserver() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); }
  virtual ~ServiceObserver() { DCHECK(CalledOnValidThread()); }

  void Initialize(const base::WeakPtr<InputServiceProxy>& proxy) {
    DCHECK(CalledOnValidThread());
    InputServiceLinux::GetInstance()->AddObserver(this);
    proxy_ = proxy;
  }

  void Shutdown() {
    DCHECK(CalledOnValidThread());
    if (InputServiceLinux::HasInstance())
      InputServiceLinux::GetInstance()->RemoveObserver(this);
    delete this;
  }

  std::vector<InputDeviceInfo> GetDevices() {
    DCHECK(CalledOnValidThread());
    std::vector<InputDeviceInfo> devices;
    if (InputServiceLinux::HasInstance())
      InputServiceLinux::GetInstance()->GetDevices(&devices);
    return devices;
  }

  void GetDeviceInfo(const std::string& id,
                     const InputServiceProxy::GetDeviceInfoCallback& callback) {
    DCHECK(CalledOnValidThread());
    bool success = false;
    InputDeviceInfo info;
    info.id = id;
    if (InputServiceLinux::HasInstance())
      success = InputServiceLinux::GetInstance()->GetDeviceInfo(id, &info);
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE, base::Bind(callback, success, info));
  }

  // InputServiceLinux::Observer implementation:
  virtual void OnInputDeviceAdded(
      const InputServiceLinux::InputDeviceInfo& info) OVERRIDE {
    DCHECK(CalledOnValidThread());
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&InputServiceProxy::OnDeviceAdded, proxy_, info));
  }

  virtual void OnInputDeviceRemoved(const std::string& id) OVERRIDE {
    DCHECK(CalledOnValidThread());
    BrowserThread::PostTask(
        BrowserThread::UI,
        FROM_HERE,
        base::Bind(&InputServiceProxy::OnDeviceRemoved, proxy_, id));
  }

 private:
  bool CalledOnValidThread() const {
    return BrowserThread::CurrentlyOn(BrowserThread::FILE);
  }

  base::WeakPtr<InputServiceProxy> proxy_;

  DISALLOW_COPY_AND_ASSIGN(ServiceObserver);
};

InputServiceProxy::InputServiceProxy()
    : service_observer_(new ServiceObserver()), weak_factory_(this) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&InputServiceProxy::ServiceObserver::Initialize,
                 base::Unretained(service_observer_.get()),
                 weak_factory_.GetWeakPtr()));
}

InputServiceProxy::~InputServiceProxy() {
  DCHECK(thread_checker_.CalledOnValidThread());
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&InputServiceProxy::ServiceObserver::Shutdown,
                 base::Unretained(service_observer_.release())));
}

void InputServiceProxy::AddObserver(Observer* observer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (observer)
    observers_.AddObserver(observer);
}

void InputServiceProxy::RemoveObserver(Observer* observer) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (observer)
    observers_.RemoveObserver(observer);
}

void InputServiceProxy::GetDevices(const GetDevicesCallback& callback) {
  DCHECK(thread_checker_.CalledOnValidThread());
  BrowserThread::PostTaskAndReplyWithResult(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&InputServiceProxy::ServiceObserver::GetDevices,
                 base::Unretained(service_observer_.get())),
      callback);
}

void InputServiceProxy::GetDeviceInfo(const std::string& id,
                                      const GetDeviceInfoCallback& callback) {
  DCHECK(thread_checker_.CalledOnValidThread());
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&InputServiceProxy::ServiceObserver::GetDeviceInfo,
                 base::Unretained(service_observer_.release()),
                 id,
                 callback));
}

void InputServiceProxy::OnDeviceAdded(
    const InputServiceLinux::InputDeviceInfo& info) {
  DCHECK(thread_checker_.CalledOnValidThread());
  FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info));
}

void InputServiceProxy::OnDeviceRemoved(const std::string& id) {
  DCHECK(thread_checker_.CalledOnValidThread());
  FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id));
}

}  // namespace chromeos