/*
**	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/>.
*/

// LevelEditDoc.cpp : implementation of the CLevelEditDoc class
//

#include "stdafx.h"
#include "leveledit.h"
#include "leveleditdoc.h"
#include "utils.h"
#include "physcoltest.h"
#include "gridcull.h"
#include "aabtreecull.h"
#include "light.h"
#include "ffactory.h"
#include "leveleditview.h"
#include "filemgr.h"
#include "profilesection.h"
#include "editorphys.h"
#include "sceneeditor.h"
#include "nodemgr.h"
#include "node.h"
#include "cameramgr.h"
#include "regkeys.h"
#include "editorassetmgr.h"
#include "_assetmgr.h"
#include "rcfile.h"
#include "editorini.h"
#include "updateassetsdialog.h"
#include "filelocations.h"
#include "ww3d.h"
#include "editorsaveload.h"
#include "presetslibform.h"
#include "rawfile.h"
#include "progressui.h"
#include "verchk.h"
#include "overlappage.h"
#include "stringsmgr.h"
#include "dazzle.h"
#include "texture.h"
#include "conversationpage.h"
#include "conversationeditormgr.h"
#include "dx8renderer.h"
#include "presetmgr.h"
#include "combatchunkid.h"
#include "combat.h"
#include "backgroundmgr.h"
#include "weathermgr.h"
#include "gameobjmanager.h"
#include "scripts.h"
#include "soundenvironment.h"
#include "soldier.h"
#include "editorbuild.h"
#include "export.h"


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


/////////////////////////////////////////////////////////////////////////////
//	Constants
/////////////////////////////////////////////////////////////////////////////
const Vector3			DEF_BACK_COLOR			(0.2F, 0.4F, 0.6F);
const Vector3			DEF_AMBIENT_COLOR		(0.3F, 0.3F, 0.3F);


/////////////////////////////////////////////////////////////////////////////
//	Static member initialization
/////////////////////////////////////////////////////////////////////////////
bool	CLevelEditDoc::IsBatchExportMode = false;


/////////////////////////////////////////////////////////////////////////////
// CLevelEditDoc

IMPLEMENT_DYNCREATE(CLevelEditDoc, CDocument)

BEGIN_MESSAGE_MAP(CLevelEditDoc, CDocument)
	//{{AFX_MSG_MAP(CLevelEditDoc)
	ON_COMMAND(ID_FILE_SAVE, OnFileSave)
	ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLevelEditDoc construction/destruction

////////////////////////////////////////////////////////////
//
//	CLevelEditDoc
//
CLevelEditDoc::CLevelEditDoc (void)
	:	m_pScene (NULL),
		m_bPerformanceSampling (false),
		m_DisplayPathWeb (false),
		m_DisplayFullPaths (false),
		m_CoordSystem (COORD_WORLD),
		m_AxisRestriction (RESTRICT_NONE),
		m_ModeModifiers (0),
		m_bSnapPointsActive (true),
		m_bNeedRepartition (false),
		m_DebugScriptsMode (false)
{
	// Allocate new managers
	m_pFileMgr = new FileMgrClass;

	//
	//	Read the settings from the registry
	//
	m_DebugScriptsMode = (theApp.GetProfileInt (CONFIG_KEY, "ScriptDebugMode", 0) == 1);
	return;
}


////////////////////////////////////////////////////////////
//
//	~CLevelEditDoc
//
CLevelEditDoc::~CLevelEditDoc (void)
{
	// Used to ensure all dynamically allocated data is freed
	CleanupResources ();
	return ;
}


///////////////////////////////////////////////////////////////
//
//  Set_Debug_Script_Mode
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Set_Debug_Script_Mode (bool onoff)
{
	m_DebugScriptsMode = onoff;
	theApp.WriteProfileInt (CONFIG_KEY, "ScriptDebugMode", m_DebugScriptsMode);
	return ;
}


///////////////////////////////////////////////////////////////
//
//  Initialize_VSS
//
bool
CLevelEditDoc::Initialize_VSS (void)
{
	bool retval = false;
	ASSERT (m_pFileMgr != NULL);

#ifdef PUBLIC_EDITOR_VER
	m_pFileMgr->Initialize_VSS ("");
	retval = true;
#else

	// Get the default VSS database to open
	CString database_path = theApp.GetProfileString (CONFIG_KEY,
																	 VSSDB_VALUE,
																	 DEF_VSS_DB);

	// Look up the user's Source Safe logon name
	TCHAR user_name[256] = { 0 };
	bool blogon_success = false;
	if (::GetEnvironmentVariable ("SSUSER", user_name, sizeof (user_name)) == 0) {

		// Let the user know they need to setup this variable
		::Message_Box (NULL,
							IDS_NOVSSUSER_MSG,
							IDS_NOVSS_TITLE,
							MB_ICONERROR | MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
	} else {

		// Attempt to log onto the database using the correct username
		blogon_success = m_pFileMgr->Initialize_VSS (database_path, user_name);
	}

	// Were we successful in logging on with the username?
	if (blogon_success == false) {
		
		// Attempt to logon with read-only access
		blogon_success = m_pFileMgr->Initialize_VSS (database_path);
		if (!blogon_success) {
		
			// If we were unsuccessful, let the user know
			::Message_Box (NULL,
								IDS_NOVSS_MSG,
								IDS_NOVSS_TITLE,
								MB_ICONERROR | MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
		}

		// Ensure or is VSS interface read-only
		m_pFileMgr->Set_Read_Only_VSS (true);
	}
	
	if (m_pFileMgr->Is_VSS_Read_Only ()) {

		// Let the user know they are in read-only mode
		::Message_Box (NULL,
							IDS_VSS_READONLY_MSG,
							IDS_NOVSS_TITLE,
							MB_ICONEXCLAMATION | MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
	}

	// If at some point we were successful, then cache the database
	// path in the registry and return success
	if (blogon_success) {
		theApp.WriteProfileString (CONFIG_KEY, VSSDB_VALUE, database_path);	
		retval = true;
	}		

#endif //PUBLIC_EDITOR_VER
	
	// Return the true/false result code
	return retval;
}


///////////////////////////////////////////////////////////////
//
//  Change_Base_Path
//
bool
CLevelEditDoc::Change_Base_Path (void)	
{
	// Assume failure
	bool retval = false;

	// Ask the user to pick a base path
	CString base_path;
	if (::Browse_For_Folder (base_path,
									 NULL,
									 NULL,
									 "Please select a location to keep your models.  A subdir named Assets will be created automatically.")) {
		
		// Concat the Assets subdir onto the path
		if (base_path[::lstrlen (base_path)-1] != '\\') {
			base_path += "\\";
		}
		base_path += "Assets";

		// Let the file manager know what the new base path is
		m_pFileMgr->Set_Base_Path (base_path);
		retval = true;
	}

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


///////////////////////////////////////////////////////////////
//
//  Update_Core_Files
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Update_Core_Files (void)
{	
	// Ensure that VSS is initialized
	if (Initialize_VSS ()) {
		
		// Do we already have a base path?
		if (::lstrlen (m_pFileMgr->Get_Base_Path ()) == 0) {
								
			// Ask the user for a base path
			if (!Change_Base_Path ()) {

				// Pick a path for the user
				CString path = ::Get_Startup_Directory ().Left (3);
				path += DEF_ASSET_PATH;
				m_pFileMgr->Set_Base_Path (path);
			}
		}

		//
		//	Add some search paths to the file factory
		//		
		EditorFileFactoryClass::Add_Search_Path (m_pFileMgr->Make_Full_Path (SHATTER_DIR));
		EditorFileFactoryClass::Add_Search_Path (m_pFileMgr->Make_Full_Path (WEATHER_DIR));
		EditorFileFactoryClass::Add_Search_Path (m_pFileMgr->Make_Full_Path (SKY_DIR));
		EditorFileFactoryClass::Add_Search_Path (m_pFileMgr->Make_Full_Path ("always\\sound\\weather"));
		EditorFileFactoryClass::Add_Search_Path (m_pFileMgr->Make_Full_Path ("always\\effects"));

		ProgressUIMgrClass::Set_Status_Text ("Updating core asset files...");

#ifdef PUBLIC_EDITOR_VER
		Update_Assets_For_Mod_Tree ();
#else

		//
		// Make sure we have the latest versions of the core files		
		//
		m_pFileMgr->Update_File (PRESETS_PATH);
		
		if (::Is_Silent_Mode () == false) {
			m_pFileMgr->Update_File (ALWAYS_INI_PATH);
			m_pFileMgr->Update_File (SERIAL_INI_PATH);
			m_pFileMgr->Update_File (UPDATE_INI_PATH);		
			m_pFileMgr->Get_Subproject (ALWAYS_INI_DIR);
			m_pFileMgr->Update_File (HUMAN_SKEL_PATH);
			m_pFileMgr->Update_File (SKY_DIR);
			m_pFileMgr->Update_File (WEATHER_DIR);
			m_pFileMgr->Update_File (SHATTER_DIR);
			m_pFileMgr->Build_Global_Include_List ();

			if (m_DebugScriptsMode == false) {
				m_pFileMgr->Update_File (SCRIPTS_PATH);
			}

			//
			// Ensure the asset tree is updated
			//
			Update_Asset_Tree ();
		}

#endif //PUBLIC_EDITOR_VER

		//
		//	Update the strings database
		//
		StringsMgrClass::Create_Database_If_Necessary ();
		StringsMgrClass::Load_Translation_Database ();
	}

	return ;
}


///////////////////////////////////////////////////////////////
//
//  Update_Assets_For_Mod_Tree
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Update_Assets_For_Mod_Tree (void)
{
	//
	//	Get all INI files to the always\INI directory
	//
	CString full_ini_path = ::Get_File_Mgr ()->Make_Full_Path (ALWAYS_INI_DIR);
	m_pFileMgr->Update_All_Files (full_ini_path, "*.ini");

	//
	//	Make sure to get the human skeleton
	//
	m_pFileMgr->Update_File ("presets\\objects.ddb");

	//
	//	Make sure to get the human skeleton
	//
	m_pFileMgr->Update_File (HUMAN_SKEL_PATH);
	return ;	
}


///////////////////////////////////////////////////////////////
//
//  Update_Asset_Tree
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Update_Asset_Tree (void)
{	
	// Get a pointer to the INI file from the asset manager
	CString path = ::Get_File_Mgr ()->Make_Full_Path (UPDATE_INI_PATH);
	EditorINIClass *pini = _pThe3DAssetManager->Get_INI (path);
	
	// Were we successful in getting a pointer to the INI file?
	ASSERT (pini != NULL);
	if (pini != NULL) {

		// Determine what the last time we updated was and when the
		// last time the database was modified
		int update_id = theApp.GetProfileInt (CONFIG_KEY, LAST_UPDATE_VALUE, 0);
		int curr_update_id = pini->Get_Int ("General", "ID");

		// Do we need to update?
		if (update_id < curr_update_id) {
			
			// Get the comments from the file
			TCHAR comments[512];
			pini->Get_TextBlock ("Comments", comments, sizeof (comments));
			for (LPSTR newline = ::strchr (comments, '|');
			     newline != NULL;
				  newline = ::strchr (newline, '|'))
			{
				newline[0] = '\r';
				newline[1] = '\n';
			}

			//
			//	Build a list of directories that we need to update...
			//
			STRING_LIST directory_list;
			bool update_all = false;
			for (int index = update_id + 1; index <= curr_update_id; index ++) {
				
				//
				//	Get the directory associated with this update-id
				//
				CString number_string;
				number_string.Format ("%d", index);
				StringClass filename;
				pini->Get_String (filename, "Details", number_string);

				//
				//	If we couldn't get the directory, then update all files
				//
				if (filename.Get_Length () > 0) {
					directory_list.Add (::Get_File_Mgr ()->Make_Full_Path (filename));
				} else {
					update_all = true;
				}
			}

			//
			// Show a dialog to the user that will ask them
			// if they want to update now.
			//
			//if (::Is_Silent_Mode () == false) {
				UpdateAssetsDialogClass dialog (CString (comments), directory_list, update_all, NULL);
				if (dialog.DoModal () == IDOK) {

					// Remember that we've updated ourselves
					theApp.WriteProfileInt (CONFIG_KEY, LAST_UPDATE_VALUE, curr_update_id);
				}
			//}
		}

		// Free the INI object
		SAFE_DELETE (pini);
	}
	
	return ;
}


///////////////////////////////////////////////////////////////
//
//  CleanupResources
//
void
CLevelEditDoc::CleanupResources (void)
{
	//
	// Release our hold on the scene
	//
	MEMBER_RELEASE (m_pScene);
	SAFE_DELETE (m_pFileMgr);
	return ;
}


////////////////////////////////////////////////////////////
//
//	OnNewDocument
//
BOOL
CLevelEditDoc::OnNewDocument (void)
{
	// Allow the base class to process this message
	if (!CDocument::OnNewDocument())
		return FALSE;

	//
	// Keep the view from refreshing
	//
	::Get_Main_View ()->Allow_Repaint (false);

	//
	// Instantiate a new scene
	//
	MEMBER_RELEASE (m_pScene);
	m_pScene = new SceneEditorClass;
	m_pScene->Set_Ambient_Light (DEF_AMBIENT_COLOR);
	m_pScene->Update_Lighting ();
	m_pScene->Enable_Collision_Detection (0, 0);
   m_pScene->Set_Sun_Light_Orientation (0, DEG_TO_RADF (50));
	m_pScene->Update_Lighting();

	// Reset sound environment, background and weather systems.
	CombatManager::Get_Sound_Environment()->Reset();
	BackgroundMgrClass::Reset();
	WeatherMgrClass::Reset();

	//
	// Reset the scene, camera, and file manager
	//	
	m_pFileMgr->Initialize ();
	::Get_Main_View ()->Reset_View ();
	::Get_Scene_Editor ()->Re_Partition_Dynamic_Culling_System ();
	SetModifiedFlag (false);

	//
	//	Force free the assets...
	//
	WW3DAssetManager::Get_Instance ()->Free_Assets ();
	TheDX8MeshRenderer.Invalidate ();
	Preload_Human_Data ();
	Preload_Tool_Assets ();	

	//
	//	Reset the overlap-sphere page
	//
	OverlapPageClass *form = ::Get_Overlap_Form ();
	if (form != NULL) {
		form->Reset_Tree ();
	}

	//
	//	Reset the conversation page
	//
	ConversationPageClass *conversation_form = ::Get_Conversation_Form ();
	if (conversation_form != NULL) {
		conversation_form->Reload_Data ();
	}

	// Allow a refresh
	::Get_Main_View ()->Allow_Repaint (true);
	::Refresh_Main_View ();
	return true;
}


////////////////////////////////////////////////////////////
//
//	Serialize
//
////////////////////////////////////////////////////////////
void
CLevelEditDoc::Serialize (CArchive& ar)
{
	SetModifiedFlag (FALSE);
	return ;
}

/////////////////////////////////////////////////////////////////////////////
// CLevelEditDoc diagnostics

#ifdef _DEBUG
void CLevelEditDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CLevelEditDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG


///////////////////////////////////////////////////////////////
//
//  Do_Version_Check
//
////////////////////////////////////////////////////////////
bool
CLevelEditDoc::Do_Version_Check (void)
{	
	bool retval = true; 

#ifndef PUBLIC_EDITOR_VER
	//
	//	Check the version of the level editor that is out on the network
	// against the version we are running.  We don't want people making modifications
	// with and older version
	//
	if (Check_Editor_Version () == false) {
		if (Message_Box (NULL, IDS_VERSION_ERROR_MSG, IDS_VERSION_ERROR_TITLE, MB_ICONEXCLAMATION | MB_OKCANCEL | MB_SETFOREGROUND | MB_SYSTEMMODAL) == IDCANCEL) {
			
			//
			//	Just for kicks, exit the process pretty violently
			//
			ExitProcess (1);
			retval = false;
		}
		m_pFileMgr->Set_Read_Only_VSS (true);
	}

#endif //PUBLIC_EDITOR_VER

	return retval;
}


///////////////////////////////////////////////////////////////
//
//  Preload_Assets
//
////////////////////////////////////////////////////////////
void
CLevelEditDoc::Preload_Assets (void)
{
	Preload_Human_Data ();
	Preload_Tool_Assets ();

	//
	//	Initialize the dazzle system
	//
	CString dazzle_ini_path = ::Get_File_Mgr ()->Make_Full_Path (DAZZLE_INI_PATH);
	FileClass * dazzle_ini_file = _TheFileFactory->Get_File (dazzle_ini_path);
	if (dazzle_ini_file) { 
		INIClass dazzle_ini (*dazzle_ini_file);
		DazzleRenderObjClass::Init_From_INI (&dazzle_ini);
		_TheFileFactory->Return_File (dazzle_ini_file);
	}

	return ;
}


///////////////////////////////////////////////////////////////
//
//  Preload_Tool_Assets
//
////////////////////////////////////////////////////////////
void
CLevelEditDoc::Preload_Tool_Assets (void)
{
	//
	// Load the tool-art textures into the asset manager
	//
	_pThe3DAssetManager->Load_Resource_Texture ("METER.TGA");
	_pThe3DAssetManager->Load_Resource_Texture ("TA_CEMENT.TGA");
	_pThe3DAssetManager->Load_Resource_Texture ("TA_FRONT.TGA");
	_pThe3DAssetManager->Load_Resource_Texture ("TA_DUMMY.TGA");

	//
	// Load the tool-art models into the asset manager
	//
	ResourceFileClass way_start (NULL, "WayStart.w3d");
	ResourceFileClass way_mid (NULL, "WayMid.w3d");
	ResourceFileClass way_end (NULL, "WayStop.w3d");
	ResourceFileClass way_r (NULL, "Way_R.w3d");
	ResourceFileClass way_g (NULL, "Way_G.w3d");
	ResourceFileClass way_b (NULL, "Way_B.w3d");
	ResourceFileClass point_light (NULL, "PointLight.w3d");
	ResourceFileClass speaker1 (NULL, "Speaker.w3d");
	ResourceFileClass speaker2 (NULL, "Speaker2.w3d");
	ResourceFileClass body_box (NULL, "BODYBOX.W3D");
	ResourceFileClass camera_model (NULL, "CAMERA.W3D");
	ResourceFileClass grid_model (NULL, "GRID.W3D");
	ResourceFileClass trans_box_model (NULL, "TRANSBOX.W3D");
	ResourceFileClass dummy_model (NULL, "DUMMY.W3D");
	ResourceFileClass cover_spot_model (NULL, "COVERSPOT.W3D");
	ResourceFileClass building_model (NULL, "BUILDINGICON.W3D");
	ResourceFileClass carmarker_model (NULL, "CARMARKER.W3D");	
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_start);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_mid);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_end);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_r);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_g);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (way_b);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (point_light);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (speaker1);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (speaker2);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (body_box);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (camera_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (grid_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (trans_box_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (dummy_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (cover_spot_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (building_model);
	WW3DAssetManager::Get_Instance()->Load_3D_Assets (carmarker_model);

	//
	//	Build a list of all the textures in the sky and weather directories
	//
	/*CString path1 = ::Get_File_Mgr()->Make_Full_Path (SKY_DIR);
	CString path2 = ::Get_File_Mgr()->Make_Full_Path (WEATHER_DIR);
	DynamicVectorClass<CString> file_list;
	::Get_File_Mgr ()->Find_Files (path1, "*.tga", file_list, false);
	::Get_File_Mgr ()->Find_Files (path2, "*.tga", file_list, false);

	//
	//	Preload all of these textures
	//
	for (int index = 0; index < file_list.Count (); index ++) {
		CString &filename = file_list[index];

		TextureClass *texture = WW3DAssetManager::Get_Instance()->Get_Texture (filename);
		REF_PTR_RELEASE (texture);
	}*/

	return ;
}


///////////////////////////////////////////////////////////////
//
//  Preload_Human_Data
//
////////////////////////////////////////////////////////////
void
CLevelEditDoc::Preload_Human_Data (void)
{
	//
	//	Make sure the human skeletons are loaded
	//	
	_pThe3DAssetManager->Load_3D_Assets (HUMAN_SKEL_PATH);

	//
	//	Force load the commando model
	//	
	PresetClass *preset = PresetMgrClass::Find_Typed_Preset (CLASSID_GAME_OBJECT_DEF_SOLDIER, "Commando");
	if (preset != NULL) {
		preset->Load_All_Assets ();
		SoldierGameObj *soldier = (SoldierGameObj *)preset->Get_Definition ()->Create ();
		if (soldier != NULL) {
			soldier->Set_Delete_Pending ();
		}
	}

	return ;
}


///////////////////////////////////////////////////////////////
//
//  Init_Scene
//
////////////////////////////////////////////////////////////
void
CLevelEditDoc::Init_Scene (void)
{
	if (m_pScene == NULL) {

		// Ensure we have the latest core files
		Update_Core_Files ();		

		//
		// Add the texture cache to the search path
		//
		EditorFileFactoryClass::Add_Search_Path (::Get_File_Mgr ()->Get_Texture_Cache_Path ());

		//
		//	Load the global conversations
		//
		ConversationEditorMgrClass::Get_Latest_Version ();
		ConversationEditorMgrClass::Load_Global_Database ();

		//
		//	Make sure we are using the right version of the editor
		//
		Do_Version_Check ();

		//
		//	Make sure certain *required* assets are loaded into memory...
		//
		Preload_Assets ();

		//
		// Instantiate a new scene
		//
		m_pScene = new SceneEditorClass;
		m_pScene->Set_Ambient_Light (DEF_AMBIENT_COLOR);
		m_pScene->Update_Lighting ();
		m_pScene->Enable_Collision_Detection (0, 0);		
	}

	return ;
}


///////////////////////////////////////////////////////////////
//
//  Get_Coord_System_Transform
//
///////////////////////////////////////////////////////////////
Matrix3D
CLevelEditDoc::Get_Coord_System_Transform (NodeClass *node)
{
	Matrix3D matrix (1);

	// What coordinate system are we working in?
	switch (m_CoordSystem) {
		
		case CLevelEditDoc::COORD_WORLD:
			matrix = Matrix3D (1);
			break;

		case CLevelEditDoc::COORD_OBJECT:
			matrix = node->Get_Transform ();
			break;

		case CLevelEditDoc::COORD_VIEW:
			matrix = ::Get_Camera_Mgr ()->Get_Camera()->Get_Transform ();
			break;
	}

	// Return the matrix
	return matrix;
}


///////////////////////////////////////////////////////////////
//
//  OnOpenDocument
//
///////////////////////////////////////////////////////////////
BOOL
CLevelEditDoc::OnOpenDocument (LPCTSTR path) 
{
	::Get_Main_View ()->Allow_Repaint (false);

	DWORD start_time = ::GetTickCount ();
	
	//
	// Start a new document
	//
	OnNewDocument ();

	//
	//	Load the level
	//
	EditorSaveLoadClass::Load_Level (path);
	SetModifiedFlag (false);

	//
	//	Reload the conversation page
	//
	ConversationPageClass *conversation_form = ::Get_Conversation_Form ();
	if (conversation_form != NULL) {
		conversation_form->Reload_Data ();
	}

	//
	//	Load the level load time
	//
	CString load_time_message;
	load_time_message.Format ("Level load took %d seconds\r\n", int((::GetTickCount () - start_time) / 1000));
	::Output_Message (load_time_message);
		
	::Get_Main_View ()->Allow_Repaint (true);
	return TRUE;
}


///////////////////////////////////////////////////////////////
//
//  Set_Current_Path
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Set_Current_Path (const char *path)
{
	m_strPathName = path;		
	return ;	
}


///////////////////////////////////////////////////////////////
//
//  OnSaveDocument
//
///////////////////////////////////////////////////////////////
BOOL
CLevelEditDoc::OnSaveDocument (LPCTSTR path) 
{	
	theApp.WriteProfileString (CONFIG_KEY,
										LAST_SAVE_DIR_VALUE,
										::Strip_Filename_From_Path (path));

	// Peform the save operation
	EditorSaveLoadClass::Save_Level (path);
	return TRUE;
}


///////////////////////////////////////////////////////////////
//
//  Repartition_If_Necessary
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::Repartition_If_Necessary (void) 
{
	if (m_bNeedRepartition) {
		
		// (gth) can't re-partition the object culling systems
		// or we'll lose hierarchical vis...
		m_pScene->Re_Partition_Static_Lights ();
		m_pScene->Re_Partition_Audio_System ();

		m_bNeedRepartition = false;
	}
		
	return ;
}


///////////////////////////////////////////////////////////////
//
//  PreCloseFrame
//
///////////////////////////////////////////////////////////////
void
CLevelEditDoc::PreCloseFrame (CFrameWnd *pFrame)
{
	//
	//	Cleanup any dangling embedded objects
	//
	PresetMgrClass::Free_All_Embedded_Nodes ();
	NetworkObjectMgrClass::Delete_Pending ();
	GameObjObserverManager::Delete_Pending ();
	ScriptManager::Destroy_Pending ();
	return ;
}


//////////////////////////////////////////////////////////////////////////
//
//	OnFileSave
//
//////////////////////////////////////////////////////////////////////////
void
CLevelEditDoc::OnFileSave (void)
{

#ifdef PUBLIC_EDITOR_VER

	if (m_strPathName.IsEmpty () || ::GetFileAttributes (m_strPathName) == 0xFFFFFFFF) {
		
		//
		//	Pick a new path and save to it
		//
		CString full_path;
		if (Pick_Save_Path (full_path)) {
			Save_Level_File (full_path);
		}

	} else {
		
		//
		//	Save to this path
		//
		Save_Level_File (m_strPathName);
	}

#else	
	CDocument::OnFileSave ();		
#endif //PUBLIC_EDITOR_VER
	
	return ;
}


//////////////////////////////////////////////////////////////////////////
//
//	OnFileSaveAs
//
//////////////////////////////////////////////////////////////////////////
void
CLevelEditDoc::OnFileSaveAs (void)
{
#ifdef PUBLIC_EDITOR_VER

	//
	//	Pick a new path and save to it
	//
	CString full_path;
	if (Pick_Save_Path (full_path)) {
		Save_Level_File (full_path);
	}

#else	
	CDocument::OnFileSaveAs ();		
#endif //PUBLIC_EDITOR_VER

	return ;
}


//////////////////////////////////////////////////////////////////////////
//
//	Pick_Save_Path
//
//////////////////////////////////////////////////////////////////////////
bool
CLevelEditDoc::Pick_Save_Path (CString &save_path)
{
	bool retval = false;

	//
	// Determine which directory we should save to
	//
	CString full_path = ::Get_File_Mgr ()->Make_Full_Path (LEVELS_ASSET_DIR);		
	_pThe3DAssetManager->Set_Current_Directory (full_path);

	//
	// Make a default filename from the name of the document
	//
	CString default_name = GetTitle ();
	if (default_name.GetLength () > 0) {
		//default_name += ".lvl";
	}

	CFileDialog dialog (	FALSE,
								".lvl",
								default_name,
								OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
								"Level Editor Files (*.lvl)|*.lvl||",
								::AfxGetMainWnd ());

	//
	// Set the path, so it opens in the correct directory
	//
	dialog.m_ofn.lpstrInitialDir = full_path;

	//
	// Ask the user where they want to save this level
	//
	if (dialog.DoModal () == IDOK) {
		
		//
		//	Return the path to the caller
		//
		retval		= true;
		save_path	= dialog.GetPathName ();
	}

	return retval;
}


//////////////////////////////////////////////////////////////////////////
//
//	Save_Level_File
//
//////////////////////////////////////////////////////////////////////////
void
CLevelEditDoc::Save_Level_File (const char *full_path)
{
	//
	// Reset the title and change the document name
	//
	SetPathName (full_path);

	//
	//	Save in level editor format
	//
	OnSaveDocument (full_path);

	//
	//	Save in game format
	//
	ExporterClass exporter;
	exporter.Export_Level_Only (full_path);
	return ;
}
