/******************************************************************************
*                            recordMyDesktop                                  *
*******************************************************************************
*                                                                             *
*            Copyright (C) 2006,2007,2008 John Varouhakis                     *
*                                                                             *
*                                                                             *
*   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA  *
*                                                                             *
*                                                                             *
*                                                                             *
*   For further information contact me at johnvarouhakis@gmail.com            *
******************************************************************************/

#include "config.h"
#include "rmd_init_encoder.h"

#include "rmd_types.h"

#include "skeleton.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>



static void m_add_fishead_packet(ogg_stream_state *m_ogg_state)
{
	fishead_packet	skel_fp;

	skel_fp.ptime_n = skel_fp.btime_n = 0;
	skel_fp.ptime_d = skel_fp.btime_d = 1000;

	add_fishead_to_stream(m_ogg_state, &skel_fp);
}


static int rmdIncrementalNaming(char **name)
{
	struct stat buff;
	char *base_name__;
	int i = 0, fname_length = strlen(*name)-4;

	base_name__ = malloc(fname_length+1);
	strncpy(base_name__, *name, fname_length);
	base_name__[fname_length] = '\0';

	//this will go on an endless loop if you have 65536?
	//files with the same name
	//or it will crash and die.anyone interested in trying ?
	while (stat(*name, &buff)==0) {
		//create new name
		char *tname = malloc(strlen(*name)+10);
		char numbuf[8];

		strcpy(tname, base_name__);
		strcat(tname, "-");
		i++;
		snprintf( numbuf, 8, "%d", i );
		strcat(tname, numbuf);
		strcat(tname, ".ogv");
		//save new name

		free(*name);
		*name = malloc(strlen(tname)+1);
		strcpy(*name, tname);
		free(tname);
	}

	free(base_name__);
	return 0;
}

void rmdInitEncoder(ProgData *pdata, EncData *enc_data_t, int buffer_ready)
{
	int y0, y1, y2, fname_length;
	ogg_stream_state m_ogg_skel;
	ogg_page skel_og_pg;
	fisbone_packet	skel_fbv,	//video fisbone packet
		   	skel_fba;	//audio fisbone packet
	const char	*fname;

	pdata->enc_data = enc_data_t;

	fname = pdata->args.filename;
	fname_length = strlen(fname);
	if (!(fname_length>4 && !strcasecmp(&fname[fname_length-3], "ogv"))) {

		char *new_name = malloc(fname_length + 5);
		strcpy(new_name, fname);
		strcat(new_name, ".ogv");

		free(pdata->args.filename);
		pdata->args.filename = new_name;
	}

	if (!pdata->args.overwrite) {
		rmdIncrementalNaming(&(pdata)->args.filename);
		fprintf(stderr, "Output file: %s\n", pdata->args.filename);
	}

	enc_data_t->fp = fopen((pdata)->args.filename, "w");
	if (enc_data_t->fp == NULL) {
		fprintf(stderr, "Cannot open file %s for writing!\n", (pdata)->args.filename);
		exit(13);
	}

	//each stream must have a unique
	srand(time(NULL));
	y0 = rand() + 1;
	y1 = rand() + 1;
	y2 = rand() + 1;
	y2 += (y1 == y2);
	y0 = (((y0 == y1) || (y0 == y2)) ? (y1 + y2) : y0);

	//init ogg streams
	//skeleton first
	ogg_stream_init(&m_ogg_skel, y0);
	m_add_fishead_packet(&m_ogg_skel);
	if (ogg_stream_pageout(&m_ogg_skel, &skel_og_pg) != 1) {
		fprintf (stderr, "Internal Ogg library error.\n");
		exit (2);
	}
	fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);
	fwrite(skel_og_pg.body, 1, skel_og_pg.body_len, enc_data_t->fp);

	ogg_stream_init(&enc_data_t->m_ogg_ts, y1);
	if (!pdata->args.nosound)
		ogg_stream_init(&enc_data_t->m_ogg_vs, y2);

	theora_info_init(&enc_data_t->m_th_inf);
	enc_data_t->m_th_inf.frame_width		= pdata->brwin.rrect.width;
	enc_data_t->m_th_inf.frame_height		= pdata->brwin.rrect.height;
	enc_data_t->m_th_inf.width			= ((enc_data_t->m_th_inf.frame_width + 15) >> 4) << 4;
	enc_data_t->m_th_inf.height			= ((enc_data_t->m_th_inf.frame_height + 15) >> 4) << 4;
	enc_data_t->m_th_inf.offset_x			= 0;
	enc_data_t->m_th_inf.offset_y			= 0;

	enc_data_t->m_th_inf.fps_numerator		= pdata->args.fps * 100.0;
	enc_data_t->m_th_inf.fps_denominator		= 100;
	enc_data_t->m_th_inf.aspect_numerator		= 1;
	enc_data_t->m_th_inf.aspect_denominator		= 1;

	enc_data_t->m_th_inf.colorspace			= OC_CS_UNSPECIFIED;
	enc_data_t->m_th_inf.pixelformat		= OC_PF_420;

	enc_data_t->m_th_inf.target_bitrate		= pdata->args.v_bitrate;
	enc_data_t->m_th_inf.quality			= pdata->args.v_quality;
	enc_data_t->m_th_inf.dropframes_p		= 0;
	enc_data_t->m_th_inf.quick_p			= 1;
	enc_data_t->m_th_inf.keyframe_auto_p		= 1;
	enc_data_t->m_th_inf.keyframe_frequency		= 64;
	enc_data_t->m_th_inf.keyframe_frequency_force	= 64;
	enc_data_t->m_th_inf.keyframe_data_target_bitrate = enc_data_t->m_th_inf.quality * 1.5;
	enc_data_t->m_th_inf.keyframe_auto_threshold	= 80;
	enc_data_t->m_th_inf.keyframe_mindistance	= 8;
	enc_data_t->m_th_inf.noise_sensitivity		= 1;
	enc_data_t->m_th_inf.sharpness			= 2;

	if (!pdata->args.nosound) {
		int ret;
		vorbis_info_init(&enc_data_t->m_vo_inf);
		ret = vorbis_encode_init_vbr(	&enc_data_t->m_vo_inf,
						pdata->args.channels,
						pdata->args.frequency,
						(float)pdata->args.s_quality*0.1);
		if (ret) {
			fprintf(stderr, "Error while setting up vorbis stream quality!\n");
			exit(2);
		}
		vorbis_comment_init(&enc_data_t->m_vo_cmmnt);
		vorbis_analysis_init(&enc_data_t->m_vo_dsp, &enc_data_t->m_vo_inf);
		vorbis_block_init(&enc_data_t->m_vo_dsp, &enc_data_t->m_vo_block);
	}

	if (theora_encode_init(&enc_data_t->m_th_st, &enc_data_t->m_th_inf)) {
		fprintf(stderr, "Theora encoder initialization failure.\n");
		exit(2);
	}

	if (theora_encode_header(&enc_data_t->m_th_st, &enc_data_t->m_ogg_pckt1)) {
		fprintf(stderr, "Theora enocde header failure.\n");
		exit(2);
	}

	ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);
	if (ogg_stream_pageout(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pg) != 1) {
		fprintf(stderr, "Internal Ogg library error.\n");
		exit(2);
	}
	fwrite(enc_data_t->m_ogg_pg.header, 1,
		   enc_data_t->m_ogg_pg.header_len,
		   enc_data_t->fp);
	fwrite(enc_data_t->m_ogg_pg.body, 1,
		   enc_data_t->m_ogg_pg.body_len,
		   enc_data_t->fp);

	theora_comment_init(&enc_data_t->m_th_cmmnt);
	theora_comment_add_tag(&enc_data_t->m_th_cmmnt, "recordMyDesktop", VERSION);
	theora_encode_comment(&enc_data_t->m_th_cmmnt, &enc_data_t->m_ogg_pckt1);
	ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);
	theora_encode_tables(&enc_data_t->m_th_st, &enc_data_t->m_ogg_pckt1);
	ogg_stream_packetin(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pckt1);

	if (!pdata->args.nosound) {
		ogg_packet header;
		ogg_packet header_comm;
		ogg_packet header_code;

		vorbis_analysis_headerout(	&enc_data_t->m_vo_dsp,
						&enc_data_t->m_vo_cmmnt,
						&header, &header_comm,
						&header_code);
		ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header);
		if (ogg_stream_pageout(&enc_data_t->m_ogg_vs, &enc_data_t->m_ogg_pg) != 1) {
			fprintf(stderr, "Internal Ogg library error.\n");
			exit(2);
		}

		fwrite(enc_data_t->m_ogg_pg.header, 1,
			   enc_data_t->m_ogg_pg.header_len,
			   enc_data_t->fp);

		fwrite(enc_data_t->m_ogg_pg.body, 1,
			   enc_data_t->m_ogg_pg.body_len,
			   enc_data_t->fp);

		ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header_comm);
		ogg_stream_packetin(&enc_data_t->m_ogg_vs, &header_code);
	}

	//fishbone packets go here
	memset(&skel_fbv, 0, sizeof(skel_fbv));
	skel_fbv.serial_no = enc_data_t->m_ogg_ts.serialno;
	skel_fbv.nr_header_packet = 3;
	skel_fbv.granule_rate_n = enc_data_t->m_th_inf.fps_numerator;
	skel_fbv.granule_rate_d = enc_data_t->m_th_inf.fps_denominator;
	skel_fbv.start_granule = 0;
	skel_fbv.preroll = 0;
	skel_fbv.granule_shift = theora_granule_shift(&enc_data_t->m_th_inf);
	add_message_header_field(&skel_fbv, "Content-Type", "video/theora");

	add_fisbone_to_stream(&m_ogg_skel, &skel_fbv);

	if (!pdata->args.nosound) {

		memset(&skel_fba, 0, sizeof(skel_fba));
		skel_fba.serial_no = enc_data_t->m_ogg_vs.serialno;
		skel_fba.nr_header_packet = 3;
		skel_fba.granule_rate_n = pdata->args.frequency;
		skel_fba.granule_rate_d = (ogg_int64_t)1;
		skel_fba.start_granule = 0;
		skel_fba.preroll = 2;
		skel_fba.granule_shift = 0;
		add_message_header_field(&skel_fba, "Content-Type", "audio/vorbis");

		add_fisbone_to_stream(&m_ogg_skel, &skel_fba);
	}

	while (1) {
		int result = ogg_stream_flush(&m_ogg_skel, &skel_og_pg);
		if (result < 0) {
			fprintf (stderr, "Internal Ogg library error.\n");
			exit(2);
		}

		if (result == 0)
			break;

		fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);
		fwrite(skel_og_pg.body, 1, skel_og_pg.body_len, enc_data_t->fp);
	}

	while (1) {
		int result = ogg_stream_flush(&enc_data_t->m_ogg_ts, &enc_data_t->m_ogg_pg);
		if (result < 0) {
			fprintf(stderr, "Internal Ogg library error.\n");
			exit(2);
		}

		if (result == 0)
			break;

		fwrite(	enc_data_t->m_ogg_pg.header, 1,
			enc_data_t->m_ogg_pg.header_len,
			enc_data_t->fp);
		fwrite(	enc_data_t->m_ogg_pg.body, 1,
			enc_data_t->m_ogg_pg.body_len,
			enc_data_t->fp);
	}

	if (!pdata->args.nosound) {
		while (1) {
			int result = ogg_stream_flush(&enc_data_t->m_ogg_vs, &enc_data_t->m_ogg_pg);
			if (result < 0) {
				fprintf(stderr, "Internal Ogg library error.\n");
				exit(2);
			}

			if (result == 0)
				break;

			fwrite(	enc_data_t->m_ogg_pg.header, 1,
				enc_data_t->m_ogg_pg.header_len,
				enc_data_t->fp);
			fwrite(	enc_data_t->m_ogg_pg.body, 1,
				enc_data_t->m_ogg_pg.body_len,
				enc_data_t->fp);
		}
	}

	//skeleton eos
	add_eos_packet_to_stream(&m_ogg_skel);
	if (ogg_stream_flush(&m_ogg_skel, &skel_og_pg) < 0) {
		fprintf(stderr, "Internal Ogg library error.\n");
		exit(2);
	}
	fwrite(skel_og_pg.header, 1, skel_og_pg.header_len, enc_data_t->fp);

	//theora buffer allocation, if any
	if (!buffer_ready) {
		enc_data_t->yuv.y = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width);
		enc_data_t->yuv.u = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width / 4);
		enc_data_t->yuv.v = malloc(enc_data_t->m_th_inf.height * enc_data_t->m_th_inf.width / 4);
		enc_data_t->yuv.y_width = enc_data_t->m_th_inf.width;
		enc_data_t->yuv.y_height = enc_data_t->m_th_inf.height;
		enc_data_t->yuv.y_stride = enc_data_t->m_th_inf.width;

		enc_data_t->yuv.uv_width = enc_data_t->m_th_inf.width / 2;
		enc_data_t->yuv.uv_height = enc_data_t->m_th_inf.height / 2;
		enc_data_t->yuv.uv_stride = enc_data_t->m_th_inf.width / 2;
	}

	theora_info_clear(&enc_data_t->m_th_inf);
}