// 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.

#ifndef CHROMECAST_MEDIA_CMA_BASE_BUFFERING_STATE_H_
#define CHROMECAST_MEDIA_CMA_BASE_BUFFERING_STATE_H_

#include <string>

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"

namespace chromecast {
namespace media {

class BufferingConfig : public base::RefCountedThreadSafe<BufferingConfig> {
 public:
  BufferingConfig(base::TimeDelta low_level_threshold,
                  base::TimeDelta high_level_threshold);

  base::TimeDelta low_level() const { return low_level_threshold_; }
  base::TimeDelta high_level() const { return high_level_threshold_; }

  void set_low_level(base::TimeDelta low_level) {
    low_level_threshold_ = low_level;
  }
  void set_high_level(base::TimeDelta high_level) {
    high_level_threshold_ = high_level;
  }

 private:
  friend class base::RefCountedThreadSafe<BufferingConfig>;
  virtual ~BufferingConfig();

  base::TimeDelta low_level_threshold_;
  base::TimeDelta high_level_threshold_;

  DISALLOW_COPY_AND_ASSIGN(BufferingConfig);
};

class BufferingState
    : public base::RefCountedThreadSafe<BufferingState> {
 public:
  typedef base::Callback<void(base::TimeDelta)> HighLevelBufferCB;

  enum State {
    kLowLevel,
    kMediumLevel,
    kHighLevel,
    kEosReached,
  };

  // Creates a new buffering state. The initial state is |kLowLevel|.
  // |state_changed_cb| is used to notify about possible state changes.
  // |high_level_buffer_cb| is used to adjust the high buffer threshold
  // when the underlying buffer is not large enough to accomodate
  // the current high buffer level.
  BufferingState(const std::string& stream_id,
                 const scoped_refptr<BufferingConfig>& config,
                 const base::Closure& state_changed_cb,
                 const HighLevelBufferCB& high_level_buffer_cb);

  // Returns the buffering state.
  State GetState() const { return state_; }

  // Invoked when the buffering configuration has changed.
  // Based on the new configuration, the buffering state might change.
  // However, |state_changed_cb_| is not triggered in that case.
  void OnConfigChanged();

  // Sets the current rendering time for this stream.
  void SetMediaTime(base::TimeDelta media_time);

  // Sets/gets the maximum rendering media time for this stream.
  // The maximum rendering time is always lower than the buffered time.
  void SetMaxRenderingTime(base::TimeDelta max_rendering_time);
  base::TimeDelta GetMaxRenderingTime() const;

  // Sets the buffered time.
  void SetBufferedTime(base::TimeDelta buffered_time);

  // Notifies the buffering state that all the frames for this stream have been
  // buffered, i.e. the end of stream has been reached.
  void NotifyEos();

  // Notifies the buffering state the underlying buffer has reached
  // its maximum capacity.
  // The maximum frame timestamp in the buffer is given by |buffered_time|.
  // Note: this timestamp can be different from the one provided through
  // SetBufferedTime since SetBufferedTime takes the timestamp of a playable
  // frame which is not necessarily the case here (e.g. missing key id).
  void NotifyMaxCapacity(base::TimeDelta buffered_time);

  // Buffering state as a human readable string, for debugging.
  std::string ToString() const;

 private:
  friend class base::RefCountedThreadSafe<BufferingState>;
  virtual ~BufferingState();

  // Returns the state solely based on the buffered time.
  State GetBufferLevelState() const;

  // Updates the state to |new_state|.
  void UpdateState(State new_state);

  std::string const stream_id_;
  scoped_refptr<BufferingConfig> const config_;

  // Callback invoked each time there is a change of state.
  base::Closure state_changed_cb_;

  // Callback invoked to adjust the high buffer level.
  HighLevelBufferCB high_level_buffer_cb_;

  // State.
  State state_;

  // Playback media time.
  // Equal to kNoTimestamp() when not known.
  base::TimeDelta media_time_;

  // Maximum rendering media time.
  // This corresponds to the timestamp of the last frame sent to the hardware
  // decoder/renderer.
  base::TimeDelta max_rendering_time_;

  // Buffered media time.
  // Equal to kNoTimestamp() when not known.
  base::TimeDelta buffered_time_;

  DISALLOW_COPY_AND_ASSIGN(BufferingState);
};

}  // namespace media
}  // namespace chromecast

#endif  // CHROMECAST_MEDIA_CMA_BASE_BUFFERING_STATE_H_
