// Copyright 2015 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 CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_H_
#define CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_H_

#include <string>
#include <vector>

#include "base/callback.h"
#include "chrome/browser/media/router/issue.h"
#include "chrome/browser/media/router/media_route.h"
#include "chrome/browser/media/router/media_sink.h"
#include "chrome/browser/media/router/media_source.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/presentation_session_message.h"

namespace media_router {

class IssuesObserver;
class MediaRoutesObserver;
class MediaSinksObserver;
class PresentationSessionMessagesObserver;

// Type of callback used in |CreateRoute()| and |JoinRoute()|. Callback is
// invoked when the route request either succeeded or failed.
// On success:
// |route|: The route created or joined.
// |presentation_id|:
//     The presentation ID of the route created or joined. In the case of
//     |CreateRoute()|, the ID is generated by MediaRouter and is guaranteed to
//     be unique.
// |error|: Empty string.
// On failure:
// |route|: nullptr
// |presentation_id|: Empty string.
// |error|: Non-empty string describing the error.
using MediaRouteResponseCallback =
    base::Callback<void(const MediaRoute* route,
                        const std::string& presentation_id,
                        const std::string& error)>;

// Used in cases where a tab ID is not applicable in CreateRoute/JoinRoute.
const int kInvalidTabId = -1;

// An interface for handling resources related to media routing.
// Responsible for registering observers for receiving sink availability
// updates, handling route requests/responses, and operating on routes (e.g.
// posting messages or closing).
class MediaRouter : public KeyedService {
 public:
  using PresentationSessionMessageCallback = base::Callback<void(
      scoped_ptr<ScopedVector<content::PresentationSessionMessage>>)>;
  using SendRouteMessageCallback = base::Callback<void(bool sent)>;

  ~MediaRouter() override = default;

  // Creates a media route from |source_id| to |sink_id|.
  // |origin| is the URL of requestor's page.
  // |tab_id| is the ID of the tab in which the request was made.
  // |origin| and |tab_id| are used for enforcing same-origin and/or same-tab
  // scope for JoinRoute() requests. (e.g., if enforced, the page
  // requesting JoinRoute() must have the same origin as the page that requested
  // CreateRoute()).
  // The caller may pass in|kInvalidTabId| if tab is not applicable.
  // Each callback in |callbacks| is invoked with a response indicating
  // success or failure, in the order they are listed.
  virtual void CreateRoute(
      const MediaSource::Id& source_id,
      const MediaSink::Id& sink_id,
      const GURL& origin,
      int tab_id,
      const std::vector<MediaRouteResponseCallback>& callbacks) = 0;

  // Joins an existing route identified by |presentation_id|.
  // |source|: The source to route to the existing route.
  // |presentation_id|: Presentation ID of the existing route.
  // |origin|, |tab_id|: Origin and tab of the join route request. Used for
  // validation when enforcing same-origin and/or same-tab scope.
  // (See CreateRoute documentation).
  // Each callback in |callbacks| is invoked with a response indicating
  // success or failure, in the order they are listed.
  virtual void JoinRoute(
      const MediaSource::Id& source,
      const std::string& presentation_id,
      const GURL& origin,
      int tab_id,
      const std::vector<MediaRouteResponseCallback>& callbacks) = 0;

  // Closes the media route specified by |route_id|.
  virtual void CloseRoute(const MediaRoute::Id& route_id) = 0;

  // Posts |message| to a MediaSink connected via MediaRoute with |route_id|.
  virtual void SendRouteMessage(const MediaRoute::Id& route_id,
                                const std::string& message,
                                const SendRouteMessageCallback& callback) = 0;

  // Sends |data| to a MediaSink connected via MediaRoute with |route_id|.
  // This is called for Blob / ArrayBuffer / ArrayBufferView types.
  virtual void SendRouteBinaryMessage(
      const MediaRoute::Id& route_id,
      scoped_ptr<std::vector<uint8>> data,
      const SendRouteMessageCallback& callback) = 0;

  // Clears the issue with the id |issue_id|.
  virtual void ClearIssue(const Issue::Id& issue_id) = 0;

 private:
  friend class IssuesObserver;
  friend class MediaSinksObserver;
  friend class MediaRoutesObserver;
  friend class PresentationSessionMessagesObserver;

  // The following functions are called by friend Observer classes above.

  // Registers |observer| with this MediaRouter. |observer| specifies a media
  // source and will receive updates with media sinks that are compatible with
  // that source. The initial update may happen synchronously.
  // NOTE: This class does not assume ownership of |observer|. Callers must
  // manage |observer| and make sure |UnregisterObserver()| is called
  // before the observer is destroyed.
  // It is invalid to register the same observer more than once and will result
  // in undefined behavior.
  // If the MRPM Host is not available, the registration request will fail
  // immediately.
  virtual void RegisterMediaSinksObserver(MediaSinksObserver* observer) = 0;

  // Removes a previously added MediaSinksObserver. |observer| will stop
  // receiving further updates.
  virtual void UnregisterMediaSinksObserver(MediaSinksObserver* observer) = 0;

  // Adds a MediaRoutesObserver to listen for updates on MediaRoutes.
  // The initial update may happen synchronously.
  // MediaRouter does not own |observer|. |UnregisterMediaRoutesObserver| should
  // be called before |observer| is destroyed.
  // It is invalid to register the same observer more than once and will result
  // in undefined behavior.
  virtual void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) = 0;

  // Removes a previously added MediaRoutesObserver. |observer| will stop
  // receiving further updates.
  virtual void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) = 0;

  // Adds the IssuesObserver |observer|.
  // It is invalid to register the same observer more than once and will result
  // in undefined behavior.
  virtual void RegisterIssuesObserver(IssuesObserver* observer) = 0;

  // Removes the IssuesObserver |observer|.
  virtual void UnregisterIssuesObserver(IssuesObserver* observer) = 0;

  // Registers |observer| with this MediaRouter. |observer| specifies a media
  // route corresponding to a presentation and will receive messages from the
  // MediaSink connected to the route. Note that MediaRouter does not own
  // |observer|. |observer| should be unregistered before it is destroyed.
  // Registering the same observer more than once will result in undefined
  // behavior.
  virtual void RegisterPresentationSessionMessagesObserver(
      PresentationSessionMessagesObserver* observer) = 0;

  // Unregisters a previously registered PresentationSessionMessagesObserver.
  // |observer| will stop receiving further updates.
  virtual void UnregisterPresentationSessionMessagesObserver(
      PresentationSessionMessagesObserver* observer) = 0;
};

}  // namespace media_router

#endif  // CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_H_