//-----------------------------------------------------------------------------
//
//  $Logfile:: /win32/cin_win.cpp                                             $
// $Revision:: 1                                                              $
//   $Author:: Burger                                                         $
//     $Date:: 12/27/00 7:56p                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /win32/cin_win.cpp                                                  $
// * 
// * 1     12/27/00 7:56p Burger
// 
// 21    8/07/99 1:53p Markd
// fixed a bunch of more code for version 1.06
// 
// 20    7/27/99 4:37p Markd
// moved check for file to top of loading function so if cinematics weren't
// present it wouldn't do the flash thing
// 
// 19    11/17/98 8:26p Markd
// Changed s_register functions so that they only do status when a passed in
// boolean is true, made the cinematic do a sound restart rather than a S_Init
// 
// 18    10/27/98 10:35p Markd
// fixed cinematics AGAIN!
// 
// 17    10/27/98 10:35p Markd
// fixed fullscreen problems
// 
// 16    10/26/98 3:39p Markd
// put in cl_forcecinematic
// 
// 15    10/26/98 2:13a Markd
// fixed mode switching twice
// 
// 14    10/26/98 12:51a Markd
// fixed a bug with finish cinematic
// 
// 13    10/25/98 9:36p Markd
// made cinematics switch to software 16-bit 320x240 fullscreen by default
// 
// 12    10/25/98 12:19a Aldie
// Adjusted some things for cinematics to work with menus
// 
// 11    10/24/98 3:50a Markd
// fixed cinematics
// 
// 10    10/23/98 12:22a Aldie
// Added finish cinematic to goto nextserver
// 
// 9     10/21/98 6:40p Markd
// made cinematics use relative path names
// 
// 8     10/19/98 12:15a Markd
// tweaked some Dshow stuff
// 
// 7     9/28/98 5:28p Markd
// Put error messages into cinematic stuff
// 
// 6     9/28/98 4:15p Markd
// took out the SetFormat command
// 
// 5     9/27/98 5:10p Markd
// Rewrote a lot of the code, now works in current window hardware and software
// 
// 4     9/27/98 3:30p Markd
// intermediate check-in
// 
// 3     9/27/98 3:19p Markd
// intermediat check in of new cinematic code
// 
// 2     6/06/98 5:38p Markd
// Added windows cinematic stuff
// 
// 1     6/06/98 2:14p Markd
// 
// 1     6/06/98 1:17p Markd
// 
// DESCRIPTION:
// Directshow player
//

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <mmsystem.h>
#define CINTERFACE
#define COBJMACROS
#define DIRECTDRAW_VERSION 0x0500
#include "ddraw.h"
#include "mmstream.h"
#include "amstream.h"
#include "ddstream.h"
#include "errors.h"
#include <assert.h>
#include "client.h"
#include "winquake.h"

#define _640_X_480_RESOLUTION 3 /* FS */

static IDirectDraw *pDD = NULL;
static IMediaStream *pPrimaryVidStream = NULL;
static IMediaStream *pPrimaryAudStream = NULL;
static IDirectDrawMediaStream *pDDStream = NULL;
static IDirectDrawSurface *pSurface = NULL;
static IDirectDrawStreamSample *pSample = NULL;
static DDSURFACEDESC movie_ddsd;
static IMultiMediaStream *pMMStream = NULL;
static int vid_modified = 0;
static int sw_prevmode = _640_X_480_RESOLUTION;
static int ref_modified = 0;
static int fullscreen_modified = 0;

extern cvar_t *fs_cddir;

#ifdef __cplusplus
extern "C" {
#endif

//HWND cl_hwnd;
qboolean cinematic_active = false;
qboolean cinematic_paused = false;

static qboolean SCR_CheckVideoChange (const char *renderer, int resolution, int fullscreen) /* FS */
{
	if ((!renderer) || (Q_stricmp(renderer, "soft")) || (resolution != _640_X_480_RESOLUTION) || (fullscreen != 1))
	{
		SCR_FinishCinematic();
		return false;
	}

	return true;
}

static HRESULT OpenMMStream(const char * pszFileName, IDirectDraw *pDD)
{
	IAMMultiMediaStream *pAMStream;
	HRESULT hr;

	pMMStream = NULL;
	#ifdef __cplusplus
	hr = CoCreateInstance( CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAMStream);
	#else
	hr = CoCreateInstance(&CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,&IID_IAMMultiMediaStream, (void **)&pAMStream);
	#endif
	if (SUCCEEDED(hr))
	{
		hr = IAMMultiMediaStream_Initialize(pAMStream, STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
		if (SUCCEEDED(hr))
		{
			hr = IAMMultiMediaStream_AddMediaStream((IAMMultiMediaStream *)pAMStream, (IUnknown *)pDD, &MSPID_PrimaryVideo, 0, NULL);
			if (SUCCEEDED(hr))
			{
				hr = IAMMultiMediaStream_AddMediaStream(pAMStream, NULL, &MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NULL);
				if (SUCCEEDED(hr))
				{
					WCHAR wPath[MAX_PATH];
					MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, wPath, sizeof(wPath)/sizeof(wPath[0]));

					hr = IAMMultiMediaStream_OpenFile(pAMStream, wPath, 0);
					if (SUCCEEDED(hr))
					{
						pMMStream = (IMultiMediaStream *) pAMStream;
						IAMMultiMediaStream_AddRef(pAMStream);
					}
				}
			}
		}
	}
	if (!SUCCEEDED(hr))
	{
		char buf[128];
		AMGetErrorText(hr, buf, sizeof(buf)-1);
		Com_Printf("OpenMMStream Error: %s\n", buf);
	}
	if (pAMStream) IAMMultiMediaStream_Release(pAMStream);
	return hr;
}

/*
==================
SCR_StopCinematic
==================
*/
void SCR_StopCinematic (void)
{
	if (cl.cinematictime)
	{
		cl.cinematictime = 0;	// done
		re.EndCinematic();
		if (pMMStream) IMultiMediaStream_Release(pMMStream);
		if (pPrimaryAudStream) IMediaStream_Release(pPrimaryAudStream);
		if (pPrimaryVidStream) IMediaStream_Release(pPrimaryVidStream);
		if (pDDStream) IDirectDrawMediaStream_Release(pDDStream);
		if (pSample) IDirectDrawStreamSample_Release(pSample);
		if (pSurface) IDirectDrawSurface_Release(pSurface);
		if (pDD) IDirectDraw_Release(pDD);
		CoUninitialize();
		SCR_FinishCinematic();
		return;
	}

	if (!sound_started)
		CL_Snd_Restart_f ();

	if (cl_forcecinematic->value)
	{
		qboolean modified = false;

		// restore our settings
		if (ref_modified)
		{
			ref_modified = 0;
			Cvar_Set("vid_ref", "gl");
			modified = true;
		}
		if (vid_modified)
		{
			Cvar_SetValue("r_8bpp", vid_modified);
			vid_modified = 0;
			modified = true;
		}
		if (sw_prevmode != _640_X_480_RESOLUTION)
		{
			Cvar_SetValue("sw_mode", sw_prevmode);
//			swmode_Modified = 0; /* FS: Don't zero it out because we don't do 320x240 any more.  Too many newer machines don't even have it available as a fullscreen mode in Windows. */
			if(!Q_stricmp(Cvar_VariableString("vid_ref"), "soft"))
			{
				modified = true;
			}
		}
		if (fullscreen_modified)
		{
			Cvar_SetValue("vid_fullscreen", 0);
			fullscreen_modified = 0;
			modified = true;
		}

		if (modified)
		{
			vid_ref->modified = true;
		}
	}
}

/*
====================
SCR_FinishCinematic

Called when either the cinematic completes, or it is aborted
====================
*/
void SCR_FinishCinematic (void)
{
	//
	// make sure we stop it
	//
	SCR_StopCinematic();

	// tell the server to advance to the next map / cinematic
	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
	SZ_Print (&cls.netchan.message, va("nextserver %i\n", cl.servercount));
}

/*
==================
SCR_RunCinematic
decode next frame
==================
*/
void SCR_RunCinematic (void)
{
	if (cl.cinematictime <= 0)
		return;

	if (cl.cinematictime)
	{
		//
		// we render until their are no more updates
		// not true right now
		//
		if (IDirectDrawStreamSample_Update(pSample, 0, NULL, NULL, 0) != S_OK)
		{
			SCR_StopCinematic ();
			return;
		}
	}
}

/*
==================
SCR_DrawCinematic

Returns true if a cinematic is active, meaning the view rendering
should be skipped
==================
*/
qboolean SCR_DrawCinematic (void)
{
	unsigned short * ptr;

	if (cl.cinematictime <= 0)
		return false;

	IDirectDrawSurface_Lock(pSurface, NULL, &movie_ddsd, DDLOCK_WAIT, NULL);

	ptr = (unsigned short *)movie_ddsd.lpSurface;
	re.DrawCinematicFrame(0, 0, viddef.width, viddef.height, movie_ddsd.dwWidth, movie_ddsd.dwHeight, movie_ddsd.lPitch, ptr);

	IDirectDrawSurface_Unlock(pSurface, ptr);

	return true;
}

/*
==================
SCR_PlayCinematic

==================
*/
void SCR_PlayCinematic (const char *arg)
{
	char name[MAX_OSPATH];
	HRESULT hr;
	FILE * fp;

	//
	// try the cd first
	//
	if (!strstr(arg, ":"))
	{
		Com_sprintf(name, sizeof(name), "%s/movies/%s", fs_cddir->string, arg);
	}
	else
	{
		strncpy(name, arg, sizeof(name) - 1);
	}
	fp = fopen(name, "rb");
	if (!fp)
	{
		//
		// try the hardrive next
		//
		Com_sprintf(name, sizeof(name), "%s/movies/%s", FS_Gamedir(), arg);
		fp = fopen(name, "rb");
		if (!fp)
		{
			// we didn't find it so lets return and not do the silly video switches
			SCR_FinishCinematic();
			return;
		}
		else
		{
			fclose(fp);
		}
	}
	else
	{
		fclose(fp);
	}

	if (cl.cinematictime > 0)
		SCR_StopCinematic();

	cl.cinematictime = 0;
	vid_modified = 0;
	ref_modified = 0;
	sw_prevmode = _640_X_480_RESOLUTION;

	if (cl_forcecinematic->value)
	{
		//
		// make sure we are in 16-bit software period
		//
		if (!stricmp(Cvar_VariableString("vid_ref"), "soft"))
		{
			//
			// a value of 0 is 16-bit any thing else is an 8-bit variant
			//
			vid_modified = Cvar_VariableValue("r_8bpp");
			sw_prevmode = Cvar_VariableValue("sw_mode");
			fullscreen_modified = Cvar_VariableValue("vid_fullscreen");
			if ( (vid_modified) || (sw_prevmode != _640_X_480_RESOLUTION) || (fullscreen_modified != 1) )
			{
				if (!fullscreen_modified)
					fullscreen_modified = 1;
				else
					fullscreen_modified = 0;
				Cvar_SetValue("r_8bpp", 0);
				Cvar_SetValue("sw_mode", _640_X_480_RESOLUTION);
				Cvar_SetValue("vid_fullscreen", 1);
				vid_ref->modified = true;
				VID_CheckChanges();
				if(!SCR_CheckVideoChange(Cvar_VariableString("vid_ref"), (int)(Cvar_VariableValue("sw_mode")), (int)(Cvar_VariableValue("vid_fullscreen")))) /* FS */
				{
					return;
				}
			}
			else
			{
				fullscreen_modified = 0;
			}
		}
		//
		// else we are in hardware
		//
		else
		{
			ref_modified = 1;
			vid_modified = Cvar_VariableValue( "r_8bpp" );
			sw_prevmode = Cvar_VariableValue( "sw_mode" );
			fullscreen_modified = Cvar_VariableValue("vid_fullscreen");
			if (!fullscreen_modified)
				fullscreen_modified = 1;
			else
				fullscreen_modified = 0;
			Cvar_Set("vid_ref", "soft");
			Cvar_SetValue("r_8bpp", 0);
			Cvar_SetValue("sw_mode", _640_X_480_RESOLUTION);
			Cvar_SetValue("vid_fullscreen", 1);
			vid_ref->modified = true;
			VID_CheckChanges();
			if(!SCR_CheckVideoChange(Cvar_VariableString("vid_ref"), (int)(Cvar_VariableValue("sw_mode")), (int)(Cvar_VariableValue("vid_fullscreen")))) /* FS */
			{
				return;
			}
			else
			{
				fullscreen_modified = 0;
			}
		}
	}

	// make sure CD isn't playing music
	CDAudio_Stop();

	S_Shutdown();

	CoInitialize(NULL);

	hr = DirectDrawCreate(NULL, &pDD, NULL);
	if (SUCCEEDED(hr))
	{
		IDirectDraw_SetCooperativeLevel(pDD, cl_hwnd, DDSCL_NORMAL);
		hr = OpenMMStream(name, pDD);
		if (SUCCEEDED(hr))
		{
			RECT rect;

			re.BeginCinematic();

			memset(&movie_ddsd, 0, sizeof(movie_ddsd));
			movie_ddsd.dwSize = sizeof(movie_ddsd);

			#ifdef __cplusplus
			hr = IMultiMediaStream_GetMediaStream(pMMStream, MSPID_PrimaryAudio, &pPrimaryAudStream);
			hr = IMultiMediaStream_GetMediaStream(pMMStream, MSPID_PrimaryVideo, &pPrimaryVidStream);
			#else
			hr = IMultiMediaStream_GetMediaStream(pMMStream,&MSPID_PrimaryAudio, &pPrimaryAudStream);
			hr = IMultiMediaStream_GetMediaStream(pMMStream,&MSPID_PrimaryVideo, &pPrimaryVidStream);
			#endif
			if (SUCCEEDED(hr))
			{
				#ifdef __cplusplus
				hr = IMediaStream_QueryInterface(pPrimaryVidStream, IID_IDirectDrawMediaStream, (void **)&pDDStream);
				#else
				hr = IMediaStream_QueryInterface(pPrimaryVidStream,&IID_IDirectDrawMediaStream, (void **)&pDDStream);
				#endif
				if (SUCCEEDED(hr))
				{
					hr = IDirectDrawMediaStream_GetFormat(pDDStream, &movie_ddsd, NULL, NULL, NULL);
					if (SUCCEEDED(hr))
					{
						hr = IDirectDrawMediaStream_CreateSample(pDDStream, NULL, NULL, DDSFF_PROGRESSIVERENDER, &pSample);
						if (SUCCEEDED(hr))
						{
							hr = IDirectDrawStreamSample_GetSurface(pSample, &pSurface, &rect);
							if (SUCCEEDED(hr))
							{
								hr = IMultiMediaStream_SetState(pMMStream, STREAMSTATE_RUN);
								if (SUCCEEDED(hr))
								{
									cls.state = ca_active;
									cl.cinematictime = Sys_Milliseconds ();
									cl.cinematicframe = 0;
								}
							}
						}
					}
				}
			}
		}
	}
	if (!SUCCEEDED(hr))
	{
		char buf[128];
		AMGetErrorText(hr, buf, sizeof(buf)-1);
		Com_Printf("SCR_PlayCinematic Error: %s\n", buf);
	}
	if (cl.cinematictime <= 0)
	{
		SCR_FinishCinematic();
		Com_Printf ("Unable to start cinematic: %s\n", arg);
	}
}

#ifdef __cplusplus
}
#endif

