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

#include "chromecast/media/cma/base/buffering_state.h"

#include <sstream>

#include "base/logging.h"
#include "media/base/buffers.h"

namespace chromecast {
namespace media {

BufferingConfig::BufferingConfig(
    base::TimeDelta low_level_threshold,
    base::TimeDelta high_level_threshold)
    : low_level_threshold_(low_level_threshold),
      high_level_threshold_(high_level_threshold) {
}

BufferingConfig::~BufferingConfig() {
}


BufferingState::BufferingState(
    const std::string& stream_id,
    const scoped_refptr<BufferingConfig>& config,
    const base::Closure& state_changed_cb,
    const HighLevelBufferCB& high_level_buffer_cb)
    : stream_id_(stream_id),
      config_(config),
      state_changed_cb_(state_changed_cb),
      high_level_buffer_cb_(high_level_buffer_cb),
      state_(kLowLevel),
      media_time_(::media::kNoTimestamp()),
      max_rendering_time_(::media::kNoTimestamp()),
      buffered_time_(::media::kNoTimestamp()) {
}

BufferingState::~BufferingState() {
}

void BufferingState::OnConfigChanged() {
  state_ = GetBufferLevelState();
}

void BufferingState::SetMediaTime(base::TimeDelta media_time) {
  media_time_ = media_time;
  switch (state_) {
    case kLowLevel:
    case kMediumLevel:
    case kHighLevel:
      UpdateState(GetBufferLevelState());
      break;
    case kEosReached:
      break;
  }
}

void BufferingState::SetMaxRenderingTime(base::TimeDelta max_rendering_time) {
  max_rendering_time_ = max_rendering_time;
}

base::TimeDelta BufferingState::GetMaxRenderingTime() const {
  return max_rendering_time_;
}

void BufferingState::SetBufferedTime(base::TimeDelta buffered_time) {
  buffered_time_ = buffered_time;
  switch (state_) {
    case kLowLevel:
    case kMediumLevel:
    case kHighLevel:
      UpdateState(GetBufferLevelState());
      break;
    case kEosReached:
      break;
  }
}

void BufferingState::NotifyEos() {
  UpdateState(kEosReached);
}

void BufferingState::NotifyMaxCapacity(base::TimeDelta buffered_time) {
  if (media_time_ == ::media::kNoTimestamp() ||
      buffered_time == ::media::kNoTimestamp()) {
    LOG(WARNING) << "Max capacity with no timestamp";
    return;
  }
  base::TimeDelta buffer_duration = buffered_time - media_time_;
  if (buffer_duration < config_->high_level())
    high_level_buffer_cb_.Run(buffer_duration);
}

std::string BufferingState::ToString() const {
  std::ostringstream s;
  s << stream_id_ << " state=" << state_
    << " media_time_ms=" << media_time_.InMilliseconds()
    << " buffered_time_ms=" << buffered_time_.InMilliseconds()
    << " low_level_ms=" << config_->low_level().InMilliseconds()
    << " high_level_ms=" << config_->high_level().InMilliseconds();
  return s.str();
}

BufferingState::State BufferingState::GetBufferLevelState() const {
  if (media_time_ == ::media::kNoTimestamp() ||
      buffered_time_ == ::media::kNoTimestamp()) {
    return kLowLevel;
  }

  base::TimeDelta buffer_duration = buffered_time_ - media_time_;
  if (buffer_duration < config_->low_level())
    return kLowLevel;
  if (buffer_duration >= config_->high_level())
    return kHighLevel;
  return kMediumLevel;
}

void BufferingState::UpdateState(State new_state) {
  if (new_state == state_)
    return;

  state_ = new_state;
  if (!state_changed_cb_.is_null())
    state_changed_cb_.Run();
}

}  // namespace media
}  // namespace chromecast
