// Copyright 2013 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 MEDIA_BASE_TEXT_RENDERER_H_
#define MEDIA_BASE_TEXT_RENDERER_H_

#include <map>
#include <set>

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "media/base/demuxer_stream.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "media/base/text_ranges.h"
#include "media/base/text_track.h"

namespace base {
class SingleThreadTaskRunner;
}

namespace media {

class TextCue;
class TextTrackConfig;

// Receives decoder buffers from the upstream demuxer, decodes them to text
// cues, and then passes them onto the TextTrack object associated with each
// demuxer text stream.
class MEDIA_EXPORT TextRenderer {
 public:
  // |task_runner| is the thread on which TextRenderer will execute.
  //
  // |add_text_track_cb] is called when the demuxer requests (via its host)
  // that a new text track be created.
  TextRenderer(
      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
      const AddTextTrackCB& add_text_track_cb);

  // Stops all operations and fires all pending callbacks.
  ~TextRenderer();

  // |ended_cb| is executed when all of the text tracks have reached
  // end of stream, following a play request.
  void Initialize(const base::Closure& ended_cb);

  // Starts text track cue decoding and rendering.
  void StartPlaying();

  // Temporarily suspends decoding and rendering, executing |callback| when
  // playback has been suspended.
  void Pause(const base::Closure& callback);

  // Discards any text data, executing |callback| when completed.
  void Flush(const base::Closure& callback);

  // Adds new |text_stream|, having the indicated |config|, to the text stream
  // collection managed by this text renderer.
  void AddTextStream(DemuxerStream* text_stream,
                     const TextTrackConfig& config);

  // Removes |text_stream| from the text stream collection.
  void RemoveTextStream(DemuxerStream* text_stream);

  // Returns true if there are extant text tracks.
  bool HasTracks() const;

 private:
  struct TextTrackState {
    // To determine read progress.
    enum ReadState {
      kReadIdle,
      kReadPending
    };

    explicit TextTrackState(scoped_ptr<TextTrack> text_track);
    ~TextTrackState();

    ReadState read_state;
    scoped_ptr<TextTrack> text_track;
    TextRanges text_ranges_;
  };

  // Callback delivered by the demuxer |text_stream| when
  // a read from the stream completes.
  void BufferReady(DemuxerStream* text_stream,
                   DemuxerStream::Status status,
                   const scoped_refptr<DecoderBuffer>& input);

  // Dispatches the decoded cue delivered on the demuxer's |text_stream|.
  void CueReady(DemuxerStream* text_stream,
                const scoped_refptr<TextCue>& text_cue);

  // Dispatched when the AddTextTrackCB completes, after having created
  // the TextTrack object associated with |text_stream|.
  void OnAddTextTrackDone(DemuxerStream* text_stream,
                          scoped_ptr<TextTrack> text_track);

  // Utility function to post a read request on |text_stream|.
  void Read(TextTrackState* state, DemuxerStream* text_stream);

  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  const AddTextTrackCB add_text_track_cb_;

  // Callbacks provided during Initialize().
  base::Closure ended_cb_;

  // Callback provided to Pause().
  base::Closure pause_cb_;

  // Simple state tracking variable.
  enum State {
    kUninitialized,
    kPausePending,
    kPaused,
    kPlaying,
    kEnded
  };
  State state_;

  typedef std::map<DemuxerStream*, TextTrackState*> TextTrackStateMap;
  TextTrackStateMap text_track_state_map_;

  // Indicates how many read requests are in flight.
  int pending_read_count_;

  // Indicates which text streams have not delivered end-of-stream yet.
  typedef std::set<DemuxerStream*> PendingEosSet;
  PendingEosSet pending_eos_set_;

  // NOTE: Weak pointers must be invalidated before all other member variables.
  base::WeakPtrFactory<TextRenderer> weak_factory_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(TextRenderer);
};

}  // namespace media

#endif  // MEDIA_BASE_TEXT_RENDERER_H_
