/*
Copyright (C) 2002 Quake done Quick

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.

*/
// movie.c -- video capturing

#include "quakedef.h"
#include "movie.h"
#include "movie_avi.h"

extern	float	scr_con_current;
extern qboolean	scr_drawloading;
extern	short	*snd_out;
extern	int	snd_linear_count, soundtime;

// Variables for buffering audio
short	capture_audio_samples[44100];	// big enough buffer for 1fps at 44100Hz
int	captured_audio_samples;

static	int	out_size, ssize, outbuf_size;
static	byte	*outbuf, *picture_buf;
static	FILE	*moviefile;

static	float	hack_ctr;

static qboolean OnChange_capture_dir (cvar_t *var, char *string);
cvar_t	capture_dir	= {"capture_dir", "capture", 0, OnChange_capture_dir};

cvar_t	capture_codec	= {"capture_codec", "0"};
cvar_t	capture_fps	= {"capture_fps", "30.0"};
cvar_t	capture_console	= {"capture_console", "1"};
cvar_t	capture_hack	= {"capture_hack", "0"};
cvar_t	capture_mp3	= {"capture_mp3", "0"};
cvar_t	capture_mp3_kbps = {"capture_mp3_kbps", "128"};

static qboolean movie_is_capturing = false;
qboolean	avi_loaded, acm_loaded;

qboolean Movie_IsActive (void)
{
	// don't output whilst console is down or 'loading' is displayed
	if ((!capture_console.value && scr_con_current > 0) || scr_drawloading)
		return false;

	// otherwise output if a file is open to write to
	return movie_is_capturing;
}

void Movie_Start_f (void)
{
	char	name[MAX_FILELENGTH], path[256];

	if (Cmd_Argc() != 2)
	{
		Con_Printf ("capture_start <filename> : Start capturing to named file\n");
		return;
	}

	Q_strncpyz (name, Cmd_Argv(1), sizeof(name));
	COM_ForceExtension (name, ".avi");

	hack_ctr = capture_hack.value;

	Q_snprintfz (path, sizeof(path), "%s/%s", capture_dir.string, name);
	if (!(moviefile = fopen(path, "wb")))
	{
		COM_CreatePath (path);
		if (!(moviefile = fopen(path, "wb")))
		{
			Con_Printf ("ERROR: Couldn't open %s\n", name);
			return;
		}
	}

	movie_is_capturing = Capture_Open (path);
}

void Movie_Stop (void)
{
	movie_is_capturing = false;
	Capture_Close ();
	fclose (moviefile);
}

void Movie_Stop_f (void)
{
	if (!Movie_IsActive())
	{
		Con_Printf ("Not capturing\n");
		return;
	}

	if (cls.capturedemo)
		cls.capturedemo = false;

	Movie_Stop ();

	Con_Printf ("Stopped capturing\n");
}

void Movie_CaptureDemo_f (void)
{
	if (Cmd_Argc() != 2)
	{
		Con_Printf ("Usage: capturedemo <demoname>\n");
		return;
	}

	Con_Printf ("Capturing %s.dem\n", Cmd_Argv(1));

	CL_PlayDemo_f ();
	if (!cls.demoplayback)
		return;

	Movie_Start_f ();
	cls.capturedemo = true;

	if (!movie_is_capturing)
		Movie_StopPlayback ();
}

void Movie_Init (void)
{
	AVI_LoadLibrary ();
	if (!avi_loaded)
		return;

	captured_audio_samples = 0;

	Cmd_AddCommand ("capture_start", Movie_Start_f);
	Cmd_AddCommand ("capture_stop", Movie_Stop_f);
	Cmd_AddCommand ("capturedemo", Movie_CaptureDemo_f);
	Cvar_Register (&capture_codec);
	Cvar_Register (&capture_fps);
	Cvar_Register (&capture_dir);
	Cvar_Register (&capture_console);
	Cvar_Register (&capture_hack);

	Cvar_Set (&capture_dir, va("%s/%s", com_basedir, capture_dir.string));

	ACM_LoadLibrary ();
	if (!acm_loaded)
		return;

	Cvar_Register (&capture_mp3);
	Cvar_Register (&capture_mp3_kbps);
}

void Movie_StopPlayback (void)
{
	if (!cls.capturedemo)
		return;

	cls.capturedemo = false;
	Movie_Stop ();
}

double Movie_FrameTime (void)
{
	double	time;

	if (capture_fps.value > 0)
		time = !capture_hack.value ? 1.0 / capture_fps.value : 1.0 / (capture_fps.value * (capture_hack.value + 1.0));
	else
		time = 1.0 / 30.0;
	return bound(1.0 / 1000, time, 1.0);
}

void Movie_UpdateScreen (void)
{
#ifdef GLQUAKE
	int	i, size = glwidth * glheight * 3;
	byte	*buffer, temp;
#else
	int	i, j, rowp;
	byte	*buffer, *p;
#endif

	if (!Movie_IsActive())
		return;

	if (capture_hack.value)
	{
		if (hack_ctr != capture_hack.value)
		{
			if (!hack_ctr)
				hack_ctr = capture_hack.value;
			else
				hack_ctr--;
			return;
		}
		hack_ctr--;
	}

#ifdef GLQUAKE
	buffer = Q_malloc (size);
	glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer);
	ApplyGamma (buffer, size);

	for (i = 0 ; i < size ; i += 3)
	{
		temp = buffer[i];
		buffer[i] = buffer[i+2];
		buffer[i+2] = temp;
	}
#else
	buffer = Q_malloc (vid.width * vid.height * 3);

	D_EnableBackBufferAccess ();

	p = buffer;
	for (i = vid.height - 1 ; i >= 0 ; i--)
	{
		rowp = i * vid.rowbytes;
		for (j = 0 ; j < vid.width ; j++)
		{
			*p++ = current_pal[vid.buffer[rowp]*3+2];
			*p++ = current_pal[vid.buffer[rowp]*3+1];
			*p++ = current_pal[vid.buffer[rowp]*3+0];
			rowp++;
		}
	}

	D_DisableBackBufferAccess ();
#endif

	Capture_WriteVideo (buffer);

	free (buffer);
}

void Movie_TransferStereo16 (void)
{
	if (!Movie_IsActive())
		return;

	// Copy last audio chunk written into our temporary buffer
	memcpy (capture_audio_samples + (captured_audio_samples << 1), snd_out, snd_linear_count * shm->channels);
	captured_audio_samples += (snd_linear_count >> 1);

	if (captured_audio_samples >= Q_rint (host_frametime * shm->speed))
	{
		// We have enough audio samples to match one frame of video
		Capture_WriteAudio (captured_audio_samples, (byte *)capture_audio_samples);
		captured_audio_samples = 0;
	}
}

qboolean Movie_GetSoundtime (void)
{
	if (!Movie_IsActive())
		return false;

	soundtime += Q_rint (host_frametime * shm->speed * (Movie_FrameTime() / host_frametime));

	return true;
}

static qboolean OnChange_capture_dir (cvar_t *var, char *string)
{
	if (Movie_IsActive())
	{
		Con_Printf ("Cannot change capture_dir whilst capturing. Use `capture_stop` to cease capturing first.\n");
		return true;
	}

	return false;
}
