// Copyright 2018 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 "ui/base/accelerators/global_media_keys_listener_win.h"

#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/events/keycodes/keyboard_code_conversion_win.h"
#include "ui/gfx/win/singleton_hwnd_hot_key_observer.h"

namespace ui {

// static
bool GlobalMediaKeysListenerWin::has_instance_ = false;

GlobalMediaKeysListenerWin::GlobalMediaKeysListenerWin(
    MediaKeysListener::Delegate* delegate)
    : delegate_(delegate) {
  DCHECK(delegate_);
  DCHECK(!has_instance_);
  has_instance_ = true;
}

GlobalMediaKeysListenerWin::~GlobalMediaKeysListenerWin() {
  has_instance_ = false;
}

bool GlobalMediaKeysListenerWin::StartWatchingMediaKey(KeyboardCode key_code) {
  DCHECK(IsMediaKeycode(key_code));

  // If the hotkey is already registered, do nothing.
  if (key_codes_hotkey_observers_.contains(key_code))
    return true;

  // Create an observer that registers a hot key for |key_code|.
  std::unique_ptr<gfx::SingletonHwndHotKeyObserver> observer =
      gfx::SingletonHwndHotKeyObserver::Create(
          base::BindRepeating(&GlobalMediaKeysListenerWin::OnWndProc,
                              base::Unretained(this)),
          key_code, /*modifiers=*/0);

  // If observer is null, then the hot key failed to register.
  bool success = !!observer;
  if (success)
    key_codes_hotkey_observers_[key_code] = std::move(observer);

  UMA_HISTOGRAM_BOOLEAN("Media.MediaKeysListener.RegisterHotKeyResult",
                        success);

  return success;
}

void GlobalMediaKeysListenerWin::StopWatchingMediaKey(KeyboardCode key_code) {
  DCHECK(IsMediaKeycode(key_code));

  // Deleting the observer automatically unregisters the hot key.
  key_codes_hotkey_observers_.erase(key_code);
}

void GlobalMediaKeysListenerWin::OnWndProc(HWND hwnd,
                                           UINT message,
                                           WPARAM wparam,
                                           LPARAM lparam) {
  // SingletonHwndHotKeyObservers should only send us hot key messages.
  DCHECK_EQ(WM_HOTKEY, static_cast<int>(message));

  int win_key_code = HIWORD(lparam);
  KeyboardCode key_code = KeyboardCodeForWindowsKeyCode(win_key_code);

  // We should only receive hot key events for keys that we're observing.
  DCHECK(key_codes_hotkey_observers_.contains(key_code));

  int modifiers = 0;
  modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
  modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
  modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
  Accelerator accelerator(key_code, modifiers);

  delegate_->OnMediaKeysAccelerator(accelerator);
}

}  // namespace ui