// Copyright (c) 2009 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 "media/player/movie.h"

#include "base/string_util.h"
#include "media/base/pipeline_impl.h"
#include "media/filters/audio_renderer_impl.h"
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "media/filters/file_data_source.h"
#include "media/filters/null_audio_renderer.h"
#include "media/player/wtl_renderer.h"

using media::AudioRendererImpl;
using media::FFmpegAudioDecoder;
using media::FFmpegDemuxer;
using media::FFmpegVideoDecoder;
using media::FileDataSource;
using media::FilterFactoryCollection;
using media::PipelineImpl;

namespace media {

Movie::Movie()
    : enable_audio_(true),
      enable_draw_(true),
      enable_dump_yuv_file_(false),
      enable_pause_(false),
      max_threads_(0),
      play_rate_(1.0f),
      movie_dib_(NULL),
      movie_hwnd_(0) {
}

Movie::~Movie() {
}

bool Movie::IsOpen() {
  return pipeline_ != NULL;
}

void Movie::SetFrameBuffer(HBITMAP hbmp, HWND hwnd) {
  movie_dib_ = hbmp;
  movie_hwnd_ = hwnd;
}

bool Movie::Open(const wchar_t* url, WtlVideoRenderer* video_renderer) {
  // Close previous movie.
  if (pipeline_) {
    Close();
  }

  // Create our filter factories.
  scoped_refptr<FilterFactoryCollection> factories =
      new FilterFactoryCollection();
  factories->AddFactory(FileDataSource::CreateFactory());
  factories->AddFactory(FFmpegAudioDecoder::CreateFactory());
  factories->AddFactory(FFmpegDemuxer::CreateFilterFactory());
  factories->AddFactory(FFmpegVideoDecoder::CreateFactory());

  if (enable_audio_) {
    factories->AddFactory(AudioRendererImpl::CreateFilterFactory());
  } else {
    factories->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
  }
  factories->AddFactory(
      new media::InstanceFilterFactory<WtlVideoRenderer>(video_renderer));

  thread_.reset(new base::Thread("PipelineThread"));
  thread_->Start();
  pipeline_ = new PipelineImpl(thread_->message_loop());

  // Create and start our pipeline.
  pipeline_->Start(factories, WideToUTF8(std::wstring(url)), NULL);
  while (true) {
    PlatformThread::Sleep(100);
    if (pipeline_->IsInitialized())
      break;
    if (pipeline_->GetError() != media::PIPELINE_OK)
      return false;
  }
  pipeline_->SetPlaybackRate(play_rate_);
  return true;
}

void Movie::Play(float rate) {
  // Begin playback.
  if (pipeline_)
    pipeline_->SetPlaybackRate(enable_pause_ ? 0.0f : rate);
  if (rate > 0.0f)
    play_rate_ = rate;
}

// Get playback rate.
float Movie::GetPlayRate() {
  return play_rate_;
}

// Get movie duration in seconds.
float Movie::GetDuration() {
  float duration = 0.f;
  if (pipeline_)
    duration = (pipeline_->GetDuration()).InMicroseconds() / 1000000.0f;
  return duration;
}

// Get current movie position in seconds.
float Movie::GetPosition() {
  float position = 0.f;
  if (pipeline_)
    position = (pipeline_->GetCurrentTime()).InMicroseconds() / 1000000.0f;
  return position;
}

// Set current movie position in seconds.
void Movie::SetPosition(float position) {
  int64 us = static_cast<int64>(position * 1000000);
  base::TimeDelta time = base::TimeDelta::FromMicroseconds(us);
  if (pipeline_)
    pipeline_->Seek(time, NULL);
}


// Set playback pause.
void Movie::SetPause(bool pause) {
  enable_pause_ = pause;
  Play(play_rate_);
}

// Get playback pause state.
bool Movie::GetPause() {
  return enable_pause_;
}

void Movie::SetAudioEnable(bool enable_audio) {
  enable_audio_ = enable_audio;
}

bool Movie::GetAudioEnable() {
  return enable_audio_;
}

void Movie::SetDrawEnable(bool enable_draw) {
  enable_draw_ = enable_draw;
}

bool Movie::GetDrawEnable() {
  return enable_draw_;
}

void Movie::SetDumpYuvFileEnable(bool enable_dump_yuv_file) {
  enable_dump_yuv_file_ = enable_dump_yuv_file;
}

bool Movie::GetDumpYuvFileEnable() {
  return enable_dump_yuv_file_;
}

// Teardown.
void Movie::Close() {
  if (pipeline_) {
    pipeline_->Stop(NULL);
    thread_->Stop();
    pipeline_ = NULL;
    thread_.reset();
  }
}

}  // namespace media