// Copyright 2016 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 CONTENT_RENDERER_MEDIA_MEDIA_DEVICES_EVENT_DISPATCHER_H_
#define CONTENT_RENDERER_MEDIA_MEDIA_DEVICES_EVENT_DISPATCHER_H_

#include <stdint.h>

#include <utility>
#include <vector>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "content/common/media/media_devices.h"
#include "content/common/media/media_devices.mojom.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_observer_tracker.h"

namespace content {

// This class implements the logic for managing media device-change
// subscriptions on the renderer process. It manages the generation of
// subscription IDs and  handles the interaction with the Mojo objects that
// communicate with the  browser process. There is at most one instance of this
// class per frame.
class CONTENT_EXPORT MediaDevicesEventDispatcher
    : public RenderFrameObserver,
      public RenderFrameObserverTracker<MediaDevicesEventDispatcher>,
      public base::SupportsWeakPtr<MediaDevicesEventDispatcher> {
 public:
  using SubscriptionId = uint32_t;
  using SubscriptionIdList = std::vector<SubscriptionId>;
  using DevicesChangedCallback =
      base::Callback<void(MediaDeviceType, const MediaDeviceInfoArray&)>;

  // Returns a weak pointer to the MediaDevicesEventDispatcher for the given
  // |render_frame|.
  static base::WeakPtr<MediaDevicesEventDispatcher> GetForRenderFrame(
      RenderFrame* render_frame);

  ~MediaDevicesEventDispatcher() override;

  // Register a callback for device-change notifications for a given device
  // type. Returns a subscription ID that can be used to cancel the
  // subscription.
  SubscriptionId SubscribeDeviceChangeNotifications(
      MediaDeviceType type,
      const DevicesChangedCallback& callback);

  // Cancels a subscription identified with |subscription_id| to device-change
  // notifications for device type |type|. If no subscription identified with
  // |subscription_id| exists for device type |type|, this function returns
  // without side effects.
  void UnsubscribeDeviceChangeNotifications(MediaDeviceType type,
                                            SubscriptionId subscription_id);

  // Register a callback for device-change notifications for any device type.
  // Returns a list of subscription IDs that can be used to cancel the
  // subscriptions.
  SubscriptionIdList SubscribeDeviceChangeNotifications(
      const DevicesChangedCallback& callback);

  // Cancels subscriptions identified by |subscription_ids| to device-change
  // notifications for all device types. This is implemented by invoking the
  // single-parameter version of UnsubscribeDeviceChangeNotifications() for all
  // device types.
  void UnsubscribeDeviceChangeNotifications(
      const SubscriptionIdList& subscription_ids);

  // Dispatches an event notification to all registered callbacks for device
  // type |type|. Should be invoked only by an object that detects changes in
  // the set of media devices.
  void DispatchDevicesChangedEvent(MediaDeviceType type,
                                   const MediaDeviceInfoArray& device_infos);

  // RenderFrameObserver override.
  void OnDestruct() override;

  void SetMediaDevicesDispatcherForTesting(
      ::mojom::MediaDevicesDispatcherHostPtr media_devices_dispatcher);

 private:
  explicit MediaDevicesEventDispatcher(RenderFrame* render_frame);

  const ::mojom::MediaDevicesDispatcherHostPtr& GetMediaDevicesDispatcher();

  SubscriptionId current_id_;

  using Subscription = std::pair<SubscriptionId, DevicesChangedCallback>;
  using SubscriptionList = std::vector<Subscription>;
  SubscriptionList device_change_subscriptions_[NUM_MEDIA_DEVICE_TYPES];

  ::mojom::MediaDevicesDispatcherHostPtr media_devices_dispatcher_;

  base::ThreadChecker thread_checker_;

  DISALLOW_COPY_AND_ASSIGN(MediaDevicesEventDispatcher);
};

}  // namespace content

#endif  // CONTENT_RENDERER_MEDIA_MEDIA_DEVICES_EVENT_DISPATCHER_H_
