/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	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 3 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, see <http://www.gnu.org/licenses/>.
*/

// LevelEdit.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "leveledit.h"

#include "mainfrm.h"
#include "leveleditdoc.h"
#include "leveleditview.h"
#include "assetmgr.h"
#include "ww3d.h"
#include "wwdebug.h"
#include "utils.h"
#include "editorassetmgr.h"
#include "_assetmgr.h"
#include "wwphystrig.h"
#include "regkeys.h"
#include "_wwaudio.h"
#include "win.h"
#include "sceneeditor.h"
#include "editorsaveload.h"
#include "generatingvisdialog.h"
#include "splash.h"
#include "ffactory.h"
#include "wwsaveload.h"
#include "wwmath.h"
#include "wwphys.h"
#include "translatedb.h"
#include "presetmgr.h"
#include "phys.h"
#include "utils.h"
#include "colorutils.h"
#include "assetpackagemgr.h"
#include "editorbuild.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
//	Local prototypes
/////////////////////////////////////////////////////////////////////////////
void	Register_Light_Icon (void);


/////////////////////////////////////////////////////////////////////////////
// CLevelEditApp

BEGIN_MESSAGE_MAP(CLevelEditApp, CWinApp)
	//{{AFX_MSG_MAP(CLevelEditApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	//}}AFX_MSG_MAP
	// Standard file based document commands
	ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLevelEditApp construction

CLevelEditApp::CLevelEditApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CLevelEditApp object

CLevelEditApp theApp;


/////////////////////////////////////////////////////////////////////////////
//
// fnMessage_Print_Func
//
/////////////////////////////////////////////////////////////////////////////
void
fnMessage_Print_Func (DebugType /*type*/, const char *message)
{
	::OutputDebugString (message);
	::Output_Message (message);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// fnAssert_Print_Func
//
/////////////////////////////////////////////////////////////////////////////
void
fnAssert_Print_Func (const char *message)
{
	::Output_Message (message);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// fnTrigger_Func
//
/////////////////////////////////////////////////////////////////////////////
bool
fnTrigger_Func (int trigger_num)
{
	// Assume not triggered
	bool retval = false;

	// Are we checking for inverted 'vis'?
	if (trigger_num == WWPHYS_TRIGGER_INVERT_VIS) {
		retval = bool(::GetKeyState (VK_F9) < 0);
	}

	// Return the true/false result code
	return retval;
}


bool File_Exists (LPCSTR filename)
{
	WIN32_FIND_DATA find_info;
   HANDLE file_handle = ::FindFirstFile (filename, &find_info);
	
	if (file_handle != INVALID_HANDLE_VALUE) {
		::FindClose (file_handle);
		return true;
	} else {
		return false;
	}
}


/////////////////////////////////////////////////////////////////////////////
//
// InitInstance
//
/////////////////////////////////////////////////////////////////////////////
BOOL
CLevelEditApp::InitInstance (void)
{
	//
	//	Get the installation directory of this application
	//
	TCHAR install_path[MAX_PATH] = { 0 };
	::GetModuleFileName (::AfxGetInstanceHandle (), install_path, sizeof (install_path));
	if (install_path[::lstrlen(install_path)-1] == '\\') {
		install_path[::lstrlen(install_path)-1] = 0;
	}

	_pThe3DAssetManager = new EditorAssetMgrClass;

	//
	//	Let the user select which asset package they wish to work with
	//
	AssetPackageMgrClass::Initialize ();
	AssetPackageMgrClass::Show_Package_Selection_UI ();

	//
	//	Display the splash screen
	//
	SplashClass splash_screen;
	splash_screen.Show (true);

	//
	//	Initialize the libraries
	//
	WWMath::Init ();
	WWPhys::Init ();
	WWSaveLoad::Init ();
	TranslateDBClass::Initialize ();	

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

#ifdef _AFXDLL
	Enable3dControls();			// Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();	// Call this when linking to MFC statically
#endif

	RegisterColorPicker (::AfxGetInstanceHandle ());
	RegisterColorBar (::AfxGetInstanceHandle ());

	// Change the registry key under which our settings are stored.
	// You should modify this string to be something appropriate
	// such as the name of your company or organization.
	SetRegistryKey(_T("Westwood Studios"));	

	//
	//	Handle the command line
	//
	LPCTSTR cmd_line = ::strstr (m_lpCmdLine, "-j=");
	if (cmd_line != NULL) {
		cmd_line += 3;

		Set_Silent_Mode (true);
	}


	LoadStdProfileSettings(9);  // Load standard INI file options (including MRU)

	::WWDebug_Install_Message_Handler (fnMessage_Print_Func);
	::WWDebug_Install_Assert_Handler (fnAssert_Print_Func);
	::WWDebug_Install_Trigger_Handler (fnTrigger_Func);

	//
	//	Disable the 3DFX logo
	//
	_putenv ("FX_GLIDE_NO_SPLASH=1");

	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views.

#ifdef PUBLIC_EDITOR_VER

	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME2,
		RUNTIME_CLASS(CLevelEditDoc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CLevelEditView));
	AddDocTemplate(pDocTemplate);

#else

	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CLevelEditDoc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CLevelEditView));
	AddDocTemplate(pDocTemplate);

#endif //PUBLIC_EDITOR_VER

	// Enable DDE Execute open
	EnableShellOpen();
	RegisterShellFileTypes(TRUE);

	// Parse command line for standard shell commands, DDE, file open
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// Dispatch commands specified on the command line
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	// The one and only window has been initialized, so show and update it.
	m_pMainWnd->ShowWindow(SW_SHOW);
	m_pMainWnd->UpdateWindow();

	// Enable drag/drop open
	m_pMainWnd->DragAcceptFiles();

	ProgramInstance	= m_hInstance;
	MainWindow			= m_pMainWnd->m_hWnd;	
	PresetMgrClass::Initialize ();

	//
	//	Update the installation key in the registry
	//
	const char *INSTALL_KEY		= "Software\\Westwood Studios\\LevelEdit\\Install";
	const char *INSTALL_VALUE	= "Exe";
	HKEY reg_key = NULL;
	if (::RegCreateKeyEx (	HKEY_CURRENT_USER,
									INSTALL_KEY,
									0L,
									NULL,
									0L,
									KEY_ALL_ACCESS,
									NULL,
									&reg_key,
									NULL) == ERROR_SUCCESS)
	{		
		::RegSetValueEx (	reg_key,
								INSTALL_VALUE,
								0L,
								REG_SZ,
								(BYTE *)install_path,
								::lstrlen (install_path) + 1);
		::RegCloseKey (reg_key);
	}

	//
	//	Register an icon with the light file
	//
	Register_Light_Icon ();

	//
	//	Handle the command line
	//
	if (cmd_line != NULL) {
		::Perform_Job (cmd_line, true);
		Set_Modified (false);
		m_pMainWnd->PostMessage (WM_CLOSE);
	}

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	public:
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
		// No message handlers
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CLevelEditApp::OnAppAbout()
{
	CAboutDlg aboutDlg;
	aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CLevelEditApp commands

/*
**
*/
void Debug_Refs(void)
{
#ifndef NDEBUG
	TRACE("Detecting Active Refs...\r\n");
   //ODS("At time %s", cMiscUtil::Get_Text_Time());
	RefCountNodeClass * first = RefCountClass::ActiveRefList.First();
	RefCountNodeClass * node = first;
	while (node->Is_Valid())
	{
		RefCountClass * obj = node->Get();
		ActiveRefStruct * ref = &(obj->ActiveRefInfo);

		bool display = true;
		int	count = 0;
		RefCountNodeClass * search = first;
		while (search->Is_Valid()) {

			if (search == node) {	// if this is not the first one
				if (count != 0) {
					display = false;
					break;
				}
			}

			RefCountClass * search_obj = search->Get();
			ActiveRefStruct * search_ref = &(search_obj->ActiveRefInfo);

			if ( ref->File && search_ref->File &&
				  !strcmp(search_ref->File, ref->File) &&
				  (search_ref->Line == ref->Line) ) {
				count++;
			} else if ( (ref->File == NULL) &&  (search_ref->File == NULL) ) {
				count++;
			}

			search = search->Next();
		}

		if ( display ) {
			TRACE ( "%d Active Ref: %s %d %p\n", count, ref->File,ref->Line,obj);

			static int num_printed = 0;
			if (++num_printed > 20) {
				TRACE( "And Many More......\n");
				break;
			}
		}

		node = node->Next();
	}
	TRACE("Done.\r\n");
   //ODS("At time %s", cMiscUtil::Get_Text_Time());
#endif
}


/////////////////////////////////////////////////////////////
//
//	ExitInstance
//
int
CLevelEditApp::ExitInstance (void)
{
	// Remove the debug handlers we installed earlier
	::WWDebug_Install_Message_Handler (NULL);
	::WWDebug_Install_Assert_Handler (NULL);
	::WWDebug_Install_Trigger_Handler (NULL);

	//
	// Free the audio manager
	//
	SAFE_DELETE (_pTheAudioManager);	

	//
	//	Shutdown the libraries
	//
	WWMath::Shutdown ();
	WWPhys::Shutdown ();
	WWSaveLoad::Shutdown ();
	TranslateDBClass::Shutdown ();
	PresetMgrClass::Shutdown ();

	//
	// Check for active refs
	//
	Debug_Refs ();
	
	// Allow the base class to process this message
	return CWinApp::ExitInstance();
}


/////////////////////////////////////////////////////////////
//
//	OnFileOpen
//
void
CLevelEditApp::OnFileOpen (void) 
{
	CFileDialog dialog (TRUE,
							  ".lvl",
							  NULL,
							  OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
							  "Level Files|*.lvl||",
							  ::AfxGetMainWnd ());
	
	//
	//	Setup the initial directory
	//
	CString full_path = theApp.GetProfileString (CONFIG_KEY, LAST_SAVE_DIR_VALUE);
	if (full_path.GetLength () != 0) {
		dialog.m_ofn.lpstrInitialDir = full_path;
	}

	//
	//	Let the user pick a file
	//
	if (dialog.DoModal () == IDOK) {
		OpenDocumentFile (dialog.GetPathName ());
	}

	// Determine which directory we should use as a default for the open
	/*CString full_path = theApp.GetProfileString (CONFIG_KEY, LAST_SAVE_DIR_VALUE);
	if (full_path.GetLength () != 0) {
		::SetCurrentDirectory (full_path);
		::Get_Current_Document ()->Set_Current_Path (full_path);
	}

	// Allow the base class to process this message
	CWinApp::OnFileOpen ();*/
	return ;	
}


/////////////////////////////////////////////////////////////
//
//	Register_Light_Icon
//
/////////////////////////////////////////////////////////////
void
Register_Light_Icon (void)
{
	DWORD disp		= 0;
	HKEY reg_key	= NULL;

	const char *LIGHT_ICON_APP_NAME	= "W3D Light";
	const char *LIGHT_ICON_KEY			= "W3D Light\\DefaultIcon";

	//
	//	Open the file extension association registry key
	//
	if (::RegCreateKeyEx (	HKEY_CLASSES_ROOT,
									".wlt",
									0,
									NULL,
									REG_OPTION_NON_VOLATILE,
									KEY_ALL_ACCESS,
									NULL,
									&reg_key,
									&disp) == ERROR_SUCCESS)
	{
		//
		//	Write the name of the application under this extension's default value
		//
		::RegSetValueEx (reg_key, NULL, 0, REG_SZ, (const BYTE *)LIGHT_ICON_APP_NAME, ::lstrlen (LIGHT_ICON_APP_NAME) + 1);
		::RegCloseKey (reg_key);

		//
		//	Open the application key
		//
		if (::RegCreateKeyEx (	HKEY_CLASSES_ROOT,
										LIGHT_ICON_KEY,
										0,
										NULL,
										REG_OPTION_NON_VOLATILE,
										KEY_ALL_ACCESS,
										NULL,
										&reg_key,
										&disp) == ERROR_SUCCESS)
		{
			//
			//	Build a string representing the path to the level editor and
			// the icon's index.
			//
			TCHAR path[MAX_PATH] = { 0 };
			::GetModuleFileName (NULL, path, MAX_PATH);

			CString icon_path;
			icon_path.Format ("%s,-%d", path, IDI_LIGHT);

			//
			//	Write the icon's location under this key
			//
			::RegSetValueEx (reg_key, NULL, 0, REG_SZ, (const BYTE *)(LPCTSTR)icon_path, ::lstrlen (icon_path) + 1);
			::RegCloseKey (reg_key);
		}
	}

	return ;
}


/////////////////////////////////////////////////////////////
//
//	PreTranslateMessage
//
//	This is a hack to ensure that translator keys are
// processed even if we are in one of the sidebar windows.
//
/////////////////////////////////////////////////////////////
BOOL
CLevelEditApp::PreTranslateMessage (MSG *pMsg) 
{
	BOOL retval = false;

	//
	//	Is this window registered for global translation?
	//
	if (pMsg->hwnd != NULL && ::GetProp (pMsg->hwnd, "TRANS_ACCS") == (HANDLE)1) {

		//
		//	Translate the accelerator
		//
		CMainFrame *main_wnd = (CMainFrame *)::AfxGetMainWnd ();
		if (main_wnd !=  NULL) {
			HACCEL accel_table = main_wnd->GetDefaultAccelerator ();
			if (::TranslateAccelerator (main_wnd->m_hWnd, accel_table, pMsg)) {
				retval = true;
			}		
		}
	} 
	
	if (retval == false) {
		retval = CWinApp::PreTranslateMessage (pMsg);
	}
		
	return retval;
}


