/*
    Copyright (C) 2012  microcai <microcai@fedoraproject.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define __STDC_CONSTANT_MACROS
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>


#include <stdio.h>
#include <cstdio>
#include <stdint.h>
#include <SDL/SDL.h>
#include <boost/filesystem.hpp>
namespace fs=boost::filesystem;

#include "player.h"
#include "source/source.h"
#include "video/video_out.h"
#include "audio/audio_out.h"

void player::init_file_source(source_context* sc)
{
	sc->init_source = file_init_source;
	sc->read_data = file_read_data;
	sc->close = file_close;
	sc->destory = file_destory;
	sc->offset = 0;
}

void player::init_torrent_source(source_context* sc)
{
	sc->init_source = bt_init_source;
	sc->read_data = bt_read_data;
	sc->video_media_info = bt_media_info;
	sc->close = bt_close;
	sc->destory = bt_destory;
	sc->offset = 0;
	sc->save_path = ".";
}

int player::open(const char* movie, int media_type)
{	// 濡傛灉鏈叧闂師鏉ョ殑濯掍綋, 鍒欏厛鍏抽棴.
	if (m_avplay || m_source);
		//close();

	// 鏈垱寤虹獥鍙? 鏃犳硶鎾斁, 杩斿洖澶辫触.
	if (!HasWindow())
		return -1;

	std::string filename(movie);

	uint64_t file_lentgh = 0;
	if (media_type == MEDIA_TYPE_FILE || media_type == MEDIA_TYPE_BT)
	{
		file_lentgh =fs::file_size(filename);
	}

	do {
		// 鍒涘缓avplay.
		m_avplay = alloc_avplay_context();
		if (!m_avplay)
		{
			::logger("allocate avplay context failed!\n");
			break;
		}

		// 鏍规嵁鎵撳紑鐨勬枃浠剁被鍨? 鍒涘缓涓嶅悓濯掍綋婧?
		if (media_type == MEDIA_TYPE_FILE)
		{
			//size_t len = strlen(filename);
			m_source = alloc_media_source(MEDIA_TYPE_FILE, filename.c_str(), filename.length()+1, file_lentgh);
			
			if (!m_source)
			{
				::logger("allocate media source failed, type is file.\n");
				break;
			}

			// 鎻掑叆鍒板獟浣撳垪琛?
			m_media_list.insert(std::make_pair(filename, filename));

			// 鍒濆鍖栨枃浠跺獟浣撴簮.
			init_file_source(m_source);
		}

		if (media_type == MEDIA_TYPE_HTTP)
		{

			m_source = alloc_media_source(MEDIA_TYPE_HTTP, filename.c_str(), filename.length()+1, 0);
			if (!m_source)
			{
				::logger("allocate media source failed, type is http.\n");
				break;
			}

			// 鎻掑叆鍒板獟浣撳垪琛?
			m_media_list.insert(std::make_pair(filename, filename));
		}

		if (media_type == MEDIA_TYPE_BT)
		{
			// 鍏堣鍙朾t绉嶅瓙鏁版嵁, 鐒跺悗浣滀负闄勫姞鏁版嵁淇濆瓨鍒板獟浣撴簮.
			FILE *fp = std::fopen(filename.c_str(), "r+b");
			if (!fp)
			{
				::logger("open torrent file \'%s\' failed!\n", filename.c_str());
				break;
			}
			char *torrent_data = (char*)malloc(file_lentgh);
			int readbytes = fread(torrent_data, 1, file_lentgh, fp);
			if (readbytes != file_lentgh)
			{
				::logger("read torrent file \'%s\' failed!\n", filename.c_str());
				break;
			}
			m_source = alloc_media_source(MEDIA_TYPE_BT, torrent_data, file_lentgh, 0);
			if (!m_source)
			{
				::logger("allocate media source failed, type is torrent.\n");
				break;
			}

			free(torrent_data);

			// 鍒濆鍖杢orrent濯掍綋婧?
			init_torrent_source(m_source);
		}

		if (media_type == MEDIA_TYPE_RTSP)
		{
			m_source = alloc_media_source(MEDIA_TYPE_RTSP, filename.c_str(), filename.length()+1, 0);
			if (!m_source)
			{
				::logger("allocate media source failed, type is rtsp.\n");
				break;
			}

			// 鎻掑叆鍒板獟浣撳垪琛?
			m_media_list.insert(std::make_pair(filename, filename));
		}

		// 鍒嗛厤闊抽鍜岃棰戠殑娓叉煋鍣?
		m_audio = alloc_audio_render();
		if (!m_audio)
		{
			::logger("allocate audio render failed!\n");
			break;
		}

		m_video = alloc_video_render(0);
		if (!m_video)
		{
			::logger("allocate video render failed!\n");
			break;
		}
		// 鍒濆鍖栭煶棰戝拰瑙嗛娓叉煋鍣?
		init_audio(m_audio);
		init_video(m_video);

		// 鍒濆鍖朼vplay.
		if (initialize(m_avplay, m_source) != 0)
		{
			::logger("initialize avplay failed!\n");
			break;
		}

		// 濡傛灉鏄痓t绫诲瀷, 鍒欏湪姝ゅ緱鍒拌棰戞枃浠跺垪琛? 骞舵坊鍔犲埌m_media_list.
		if (media_type == MEDIA_TYPE_BT)
		{
			int i = 0;
			media_info *media = m_avplay->m_source_ctx->media;
			for (; i < m_avplay->m_source_ctx->media_size; i++)
			{
				std::string name;
				name = media->name;
				m_media_list.insert(std::make_pair(filename, name));
			}
		}

		// 閰嶇疆闊抽瑙嗛娓叉煋鍣?
		configure(m_avplay, m_video, VIDEO_RENDER);
		configure(m_avplay, m_audio, AUDIO_RENDER);

		// 寰楀埌瑙嗛瀹介珮.
		if (m_avplay->m_video_ctx)
		{
			m_video_width = m_avplay->m_video_ctx->width;
			m_video_height = m_avplay->m_video_ctx->height;
		}

		// 鎵撳紑瑙嗛瀹炴椂鐮佺巼鍜屽抚鐜囪绠?
		enable_calc_frame_rate(m_avplay);
		enable_calc_bit_rate(m_avplay);

		return 0;

	} while (0);

	if (m_avplay)
		free_avplay_context(m_avplay);
	m_avplay = NULL;
	if (m_source)
		free_media_source(m_source);
	if (m_audio)
		free_audio_render(m_audio);
	if (m_video)
		free_video_render(m_video);

	::logger("open avplay failed!\n");

	return -1;
}

void player::init_video(vo_context* vo)
{
	//鍒涘缓绗竴涓獥鍙?	vo->user_data = SDL_SetVideoMode(800, 600, 32, SDL_RESIZABLE);

	vo->init_video = sdl_init_video;
	//m_draw_frame = sdl_render_one_frame;
	vo->re_size = sdl_re_size;
	vo->aspect_ratio = sdl_aspect_ratio;
	vo->use_overlay = sdl_use_overlay;
	vo->destory_video = sdl_destory_render;
	vo->render_one_frame = sdl_render_one_frame;//()  &draw_frame;

	::logger("init video render to sdl.\n");
}

void player::init_audio(ao_context* ao)
{
	ao->init_audio = sdl_init_audio;
	ao->play_audio = sdl_play_audio;
	ao->audio_control = sdl_audio_control;
	ao->mute_set = sdl_mute_set;
	ao->destory_audio = sdl_destory_audio;
	logger("using audio output sdl.\n");
}

bool player::play(double fact, int index)
{
	// 閲嶅鎾斁, 杩斿洖閿欒.
	if (m_cur_index == index)
		return false;

	// 濡傛灉鏄枃浠舵暟鎹? 鍒欑洿鎺ユ挱鏀?
	if (::av_start(m_avplay, fact, index) != 0)
		return false;

	m_cur_index = index;

	return true;
}

bool player::wait_for_completion()
{
	if (m_avplay)
	{
		::wait_for_completion(m_avplay);
		::logger("play completed.\n");
		return true;
	}
	return false;
}

void player::fwd()
{	
	av_seek(m_avplay,av_curr_play_time(m_avplay) / av_duration(m_avplay) + 0.05);
}

void player::bwd()
{
	double p = av_curr_play_time(m_avplay) / av_duration(m_avplay) - 0.05;
	if ( p < 0.0 )
	 p = 0.0;
	av_seek(m_avplay,p);
}

void player::resize(int w, int h)
{
	m_video->re_size(m_video, w, h);
}

void player::togglefs()
{
	if ( m_fs = ! m_fs ){
		m_avplay->m_video_st->codec->width;
		m_avplay->m_video_st->codec->height;

		this->resize(m_avplay->m_video_st->codec->width,m_avplay->m_video_st->codec->height);
	}else{
		SDL_Rect** mode = SDL_ListModes(NULL,SDL_FULLSCREEN);
		this->resize(mode[0]->w, mode[0]->h);
	}
}