//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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 3 of the License, or (at your option) any later version.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

/****************************************************************************
*
* FILE
*     MpgSet.cpp
*
* DESCRIPTION
*     Mpeg movie settings manager
*
* PROGRAMMER
*     Denzil E. Long, Jr.
*
* DATE
*     June 30, 1998
*
****************************************************************************/

#include "function.h"

#ifdef DVD
#include "mpgset.h"

#ifdef MCIMPEG
bool EnumMCI(MCIDevice* desc, void* context);
#endif

/****************************************************************************
*
* NAME
*     MPGSettings(DeviceName)
*
* DESCRIPTION
*     Default constructor
*
* INPUTS
*     DeviceName - Initial device to use for MPG playback (NULL = DXMedia)
*
* RESULT
*     NONE
*
****************************************************************************/

MPGSettings::MPGSettings(const char* deviceName)
	: mDeviceName(NULL)
	{
	SetDeviceName(deviceName);

	#ifdef MCIMPEG
	int count = mMCI.GetDeviceCount();

	mMCIDevices = NULL;
	mCount = 0;
	
	if (count)
		{
		mMCIDevices = new MCIDevice[count];
		
		if (mMCIDevices)
			{
			mMCI.EnumerateDevices(EnumMCI, this);
			}
		}
	#endif
	}


MPGSettings::MPGSettings(FileClass& file)
	: mDeviceName(NULL)
	{
	INIClass ini;
	char buffer[256];
	char* device = NULL;

	#ifdef MCIMPEG
	int count = mMCI.GetDeviceCount();

	mMCIDevices = NULL;
	mCount = 0;
	
	// Enumerate all the MCI devices that can play a movie
	if (count)
		{
		mMCIDevices = new MCIDevice[count];

		if (mMCIDevices)
			{
			mMCI.EnumerateDevices(EnumMCI, this);
			}
		}

	#endif
	
	// Retrieve the user specified device from the config file
	buffer[0] = '\0';

	if (ini.Load(file))
		{
		ini.Get_String("MovieSettings", "Device", "Default", buffer, sizeof(buffer));
		}

	// If there is a specification in the config and it isn't the default
	if ((strlen(buffer) != 0) && (stricmp(buffer, "Default") != 0))
		{
		#ifdef MCIMPEG
		// Search for selection
		for (int i = 0; i < mCount; i++)
			{
			if (stricmp(buffer, mMCIDevices[i].name) == 0)
				{
				device = mMCIDevices[i].name;
				break;
				}
			}
		#endif
		}

	SetDeviceName(device);
	}


/****************************************************************************
*
* NAME
*     ~MPGSettings
*
* DESCRIPTION
*     Destructor
*
* INPUTS
*     NONE
*
* RESULT
*     NONE
*
****************************************************************************/

MPGSettings::~MPGSettings(void)
	{
	if (mDeviceName)
		free(mDeviceName);
		
	#ifdef MCIMPEG
	if (mMCIDevices)
		delete[] mMCIDevices;
	#endif
	}


/****************************************************************************
*
* NAME
*     SetDeviceName(DeviceName)
*
* DESCRIPTION
*     Change current device used for mpeg playback
*
* INPUTS
*     DeviceName - Device name type (IE: mpegvideo)
*
* RESULT
*     NONE
*
****************************************************************************/

void MPGSettings::SetDeviceName(const char* deviceName)
	{
	if (mDeviceName)
		free(mDeviceName);

	mDeviceName = NULL;

	if (deviceName)
		mDeviceName = strdup(deviceName);
	}


bool MPGSettings::Save(FileClass& file)
	{
	INIClass ini;

	if (ini.Load(file))
		{
		const char* device = GetDeviceName();
		
		if (device)
			{
			ini.Put_String("MovieSettings", "Device", device);
			}
		else
			{
			ini.Put_String("MovieSettings", "Device", "Default");
			}
			
		ini.Save(file);
		return true;
		}

	return false;
	}


/****************************************************************************
*
* NAME
*     Dialog()
*
* DESCRIPTION
*     Mpeg playback settings dialog
*
* INPUTS
*     NONE
*
* RESULT
*     NONE
*
****************************************************************************/

void MPGSettings::Dialog(void)
	{
	//	Dialog & button dimensions
	int d_dialog_w = 200 *RESFACTOR;
	int d_dialog_h = 100 *RESFACTOR;
	int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2);
	int d_dialog_y = 70 * RESFACTOR;
	int d_dialog_cx = d_dialog_x + (d_dialog_w / 2);

	int d_txt6_h = 7 *RESFACTOR;
	int d_margin = 7 *RESFACTOR;

	int d_okay_w = 40 *RESFACTOR;
	int d_okay_h = 9 *RESFACTOR;
	int d_okay_x = d_dialog_x + d_margin + 20;
	int d_okay_y = ((d_dialog_y + d_dialog_h) - (d_okay_h + 20));

	int d_test_w = 40 *RESFACTOR;
	int d_test_h = 9 *RESFACTOR;
	int d_test_x = (d_dialog_cx - (d_test_w / 2));
	int d_test_y = ((d_dialog_y + d_dialog_h) - (d_test_h + 20));

	int d_cancel_w = 40 *RESFACTOR;
	int d_cancel_h = 9 *RESFACTOR;
	int d_cancel_x = ((d_dialog_x + d_dialog_w) - (d_cancel_w + d_margin + 20));
	int d_cancel_y = ((d_dialog_y + d_dialog_h) - (d_cancel_h + 20));

	int d_method_w = 165 * RESFACTOR;
	int d_method_h = 50 * RESFACTOR;
	int d_method_x = (d_dialog_cx - (d_method_w / 2));
	int d_method_y = (d_dialog_y + 40);

	//	Button enumerations:
	enum
		{
		BUTTON_OKAY = 100,
		BUTTON_TEST,
		BUTTON_CANCEL,
		BUTTON_METHOD,
		NUM_OF_BUTTONS = 4,
		};

	int num_of_buttons = NUM_OF_BUTTONS;
	
	//	Redraw values: in order from "top" to "bottom" layer of the dialog
	typedef enum
		{
		REDRAW_NONE = 0,
		REDRAW_BUTTONS,		// includes map interior & coord values
		REDRAW_BACKGROUND,	// includes box, map bord, key, coord labels, btns
		REDRAW_ALL = REDRAW_BACKGROUND
		} RedrawType;

	//	Dialog variables:
	KeyNumType input;								// input from user
	bool process;									// loop while true
	RedrawType display;							// true = re-draw everything
	int selection;
	bool pressed;
	int curbutton;
	TextButtonClass * buttons[NUM_OF_BUTTONS];
	int i;
	char* origDevice = NULL;

	//	Buttons
	ControlClass * commands = NULL;				// the button list

	TextButtonClass okaybtn(BUTTON_OKAY, TXT_OK, TPF_BUTTON,
		d_okay_x, d_okay_y, d_okay_w, d_okay_h);

	TextButtonClass testbtn(BUTTON_TEST, "Test", TPF_BUTTON,
		d_test_x, d_test_y, d_test_w, d_test_h);

	TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON,
		d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h);

	ListClass method(BUTTON_METHOD, d_method_x, d_method_y, d_method_w,
		d_method_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"));
		
	//	Initialize
	Set_Logic_Page(SeenBuff);
	
	//	Create the list
	commands = &okaybtn;
	testbtn.Add_Tail(*commands);
	cancelbtn.Add_Tail(*commands);
	method.Add_Tail(*commands);

	//	Fill array of button ptrs
	curbutton = 0;
	buttons[0] = &okaybtn;
	buttons[1] = &testbtn;
	buttons[2] = &cancelbtn;
	buttons[curbutton]->Turn_On();

	Keyboard->Clear();

	Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK,
		TPF_CENTER | TPF_TEXT);

	// Add device to device control
	method.Add_Item("DirectX Media (Default)");

	#ifdef MCIMPEG
	for (i = 0; i < mCount; i++)
		{
		if (mMCIDevices[i].description != NULL)
			{
			method.Add_Item(mMCIDevices[i].description);
			}
		}
	#endif
	
	method.Set_Selected_Index(0);
	
	#ifdef MCIMPEG
	// Search for current selection
	if (GetDeviceName())
		{
		for (i = 0; i < mCount; i++)
			{
			if (stricmp(GetDeviceName(), mMCIDevices[i].name) == 0)
				{
				method.Set_Selected_Index(i + 1);
				break;
				}
			}
		}
	#endif
	
	// Save original device selection
	if (GetDeviceName())
		origDevice = strdup(GetDeviceName());
						
	//------------------------------------------------------------------------
	//	Main Processing Loop
	//------------------------------------------------------------------------
	display = REDRAW_ALL;
	process = true;
	pressed = false;
	
	while (process)
		{
		#ifdef WIN32
		/*
		** If we have just received input focus again after running in the background then
		** we need to redraw.
		*/
		if (AllSurfaces.SurfacesRestored)
			{
			AllSurfaces.SurfacesRestored=FALSE;
			display = REDRAW_ALL;
			}
		#endif

		//	Invoke game callback
		Call_Back();

		//	Refresh display if needed
		if (display)
			{
			Hide_Mouse();
			
			if (display >= REDRAW_BACKGROUND)
				{
				//	Refresh the backdrop
				Load_Title_Page(true);
				CCPalette.Set();

				//	Draw the background
				Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h);
				Draw_Caption("Movie Settings", d_dialog_x, d_dialog_y, d_dialog_w);
				}

			//	Redraw buttons
			if (display >= REDRAW_BUTTONS)
				commands->Flag_List_To_Redraw();
				
			Show_Mouse();
			display = REDRAW_NONE;
			}

		//	Get user input
		input = commands->Input();

		//	Process input
		switch (input)
			{
			case (BUTTON_OKAY | KN_BUTTON):
				selection = BUTTON_OKAY;
				pressed = true;
			break;

			case (BUTTON_TEST | KN_BUTTON):
				selection = BUTTON_TEST;
				pressed = true;
			break;

			case (KN_ESC):
			case (BUTTON_CANCEL | KN_BUTTON):
				selection = BUTTON_CANCEL;
				pressed = true;
			break;

			case KN_UP:
				buttons[curbutton]->Turn_Off();
				buttons[curbutton]->Flag_To_Redraw();
				curbutton--;
				
				if (curbutton < 0)
					curbutton = (num_of_buttons - 1);
					
				buttons[curbutton]->Turn_On();
				buttons[curbutton]->Flag_To_Redraw();
			break;

			case KN_DOWN:
				buttons[curbutton]->Turn_Off();
				buttons[curbutton]->Flag_To_Redraw();
				curbutton++;
				
				if (curbutton > (num_of_buttons - 1))
					curbutton = 0;
					
				buttons[curbutton]->Turn_On();
				buttons[curbutton]->Flag_To_Redraw();
			break;

			case KN_RETURN:
				selection = curbutton + BUTTON_OKAY;
				pressed = true;
			break;

			default:
			break;
			}

		if (pressed)
			{
			// to make sure the selection is correct in case they used the mouse
			buttons[curbutton]->Turn_Off();
			buttons[curbutton]->Flag_To_Redraw();

			curbutton = selection - BUTTON_OKAY;
			buttons[curbutton]->Turn_On();
			buttons[curbutton]->IsPressed = true;
			buttons[curbutton]->Draw_Me(true);

			switch (selection)
				{
				case (BUTTON_TEST):
					buttons[curbutton]->IsPressed = false;

					if (method.Current_Index() == 0)
						{
						SetDeviceName(NULL);
						}
					else
						{
						i = method.Current_Index();
						#ifdef MCIMPEG
						SetDeviceName(mMCIDevices[i - 1].name);
						#endif
						}

					Theme.Fade_Out();
					Hide_Mouse();
					VisiblePage.Clear();
					//PlayMpegMovie("acrop");	//PG
					Keyboard->Clear();
					Show_Mouse();
					Theme.Queue_Song(THEME_CRUS);
					display = REDRAW_ALL;

					buttons[curbutton]->Turn_Off();
					buttons[curbutton]->Flag_To_Redraw();
					buttons[0]->Turn_On();
					curbutton = 0;
				break;

				case (BUTTON_OKAY):
					process = false;
					
					if (method.Current_Index() == 0)
						{
						SetDeviceName(NULL);
						}
					else
						{
						i = method.Current_Index();
						#ifdef MCIMPEG
						SetDeviceName(mMCIDevices[i - 1].name);
						#endif
						}

					{
					RawFileClass file(CONFIG_FILE_NAME);
					Save(file);
					}
				break;

				case (BUTTON_CANCEL):
					process = false;
					SetDeviceName(origDevice);
				break;
				}

			pressed = false;
			}
		}

	if (origDevice)
		free(origDevice);
	}


#ifdef MCIMPEG
/****************************************************************************
*
* NAME
*     EnumMCI(DeviceDesc, Context)
*
* DESCRIPTION
*     MCI device enumeration callback
*
* INPUTS
*     DeviceDesc - MCI device description
*     Context    - User defined context variable
*
* RESULT
*     Continue - Continue with next device flag
*
****************************************************************************/

bool EnumMCI(MCIDevice* desc, void* context)
	{
	MPGSettings* mpgset = (MPGSettings*)context;

	// Digital video device type?
	if (desc->type == MCI_DEVTYPE_DIGITAL_VIDEO)
		{
		if (MciMovie)
			{
			CCFileClass file;
			const char* filename;

			filename = file.Set_Name("movies\\acrop.mpg");
	
			if (!file.Is_Available())
				{
				char buffer[256];
				sprintf(buffer, "Couldn't test MCI device %s\n", desc->name);
				VisiblePage.Clear();
				GamePalette.Set();
				Show_Mouse();
				WWMessageBox().Process(buffer);
				Hide_Mouse();
				VisiblePage.Clear();
				return true;
				}
			
			if (MciMovie->Open(filename, desc->name))
				{
				MciMovie->Close();
				memcpy((void*)&mpgset->mMCIDevices[mpgset->mCount], (void*)desc,
					sizeof(MCIDevice));
				mpgset->mCount++;
				}
			}
		}

	return true;
	}
#endif
#endif