//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
// PrefabsDlg.cpp : implementation file
//

#include "stdafx.h"
#include "hammer.h"
#include "PrefabsDlg.h"
#include "Prefabs.h"
#include "Prefab3D.h"
#include "EditPrefabDlg.h"
#include "MapDoc.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

/////////////////////////////////////////////////////////////////////////////
// CPrefabsDlg dialog

CPrefabsDlg::CPrefabsDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CPrefabsDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CPrefabsDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}


void CPrefabsDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPrefabsDlg)
	DDX_Control(pDX, IDC_OBJECTS, m_Objects);
	DDX_Control(pDX, IDC_OBJECTNOTES, m_ObjectNotes);
	DDX_Control(pDX, IDC_LIBRARYNOTES, m_LibraryNotes);
	DDX_Control(pDX, IDC_LIBRARIES, m_Libraries);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CPrefabsDlg, CDialog)
	//{{AFX_MSG_MAP(CPrefabsDlg)
	ON_BN_CLICKED(IDC_ADDLIBRARY, OnAddlibrary)
	ON_BN_CLICKED(IDC_ADDOBJECT, OnAddobject)
	ON_BN_CLICKED(IDC_EDITLIBRARY, OnEditlibrary)
	ON_BN_CLICKED(IDC_EDITOBJECT, OnEditobject)
	ON_BN_CLICKED(IDC_EXPORTOBJECT, OnExportobject)
	ON_CBN_SELCHANGE(IDC_LIBRARIES, OnSelchangeLibraries)
	ON_BN_CLICKED(IDC_REMOVELIBRARY, OnRemovelibrary)
	ON_BN_CLICKED(IDC_REMOVEOBJECT, OnRemoveobject)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_OBJECTS, OnItemchangedObjects)
	ON_NOTIFY(LVN_ENDLABELEDIT, IDC_OBJECTS, OnEndlabeleditObjects)
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP
	ON_COMMAND_EX(id_EditObjectInfo, HandleEditObjectPopup)
	ON_COMMAND_EX(id_EditObjectData, HandleEditObjectPopup)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPrefabsDlg message handlers

void CPrefabsDlg::OnAddobject() 
{
	CPrefabLibrary *pLibrary = GetCurrentLibrary();
	if(!pLibrary)
		return;	// no lib, no add

	CFileDialog dlg(TRUE, NULL, NULL, OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST |
		OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_NOCHANGEDIR, 
		"Prefab files (*.map;*.rmf;*.os)|*.map; *.rmf; *.os|"
		"Game MAP files (*.map)|*.map|"
		"Worldcraft RMF files (*.rmf)|*.rmf||", this);

	if(dlg.DoModal() == IDCANCEL)
		return;	// aborted

	// add all these files .. 
	char szDir[MAX_PATH], szFiles[2048];
	memcpy(szFiles, dlg.m_ofn.lpstrFile, dlg.m_ofn.nMaxFile);
	strcpy(szDir, dlg.m_ofn.lpstrFile);

	BOOL bOneFile = FALSE;
	char *p = szFiles + strlen(szDir) + 1;
	if(!p[0])
	{
		bOneFile = TRUE;
		p = szDir;	// just one file
	}

	// disable caching of prefabs
	CPrefab::EnableCaching(FALSE);

	// get files
	char szFile[MAX_PATH];
	CString strFullPath;
	int iItem = m_Objects.GetItemCount();
	while(1)
	{
		strcpy(szFile, p);
		if(!szFile[0])
			break;
		p += strlen(szFile) + 1;

		if(!bOneFile)
			strFullPath.Format("%s\\%s", szDir, szFile);
		else
			strFullPath = szFile;

		// check file type
		CPrefab *pPrefab = NULL;

		switch(CPrefab::CheckFileType(strFullPath))
		{
			case CPrefab::pftUnknown:
			{
				continue;	// no.
			}

			case CPrefab::pftRMF:
			{
				CPrefabRMF *pNew = new CPrefabRMF;
				pNew->Init(strFullPath, TRUE, CPrefab::lsRMF);
				pPrefab = (CPrefab *)pNew;
				break;
			}

			case CPrefab::pftMAP:
			{
				CPrefabRMF *pNew = new CPrefabRMF;
				pNew->Init(strFullPath, TRUE, CPrefab::lsMAP);
				pPrefab = (CPrefab *)pNew;
				break;
			}

			case CPrefab::pftScript:
			{
				Assert(0);	// not supported yet
				break;
			}
		}

		if (!pPrefab)
		{
			continue;
		}

		// add to current library
		pLibrary->Add(pPrefab);
		// add to objects list
		AddToObjectList(pPrefab, iItem++);

		if(bOneFile)
			break;
	}

	// now rewrite library
	pLibrary->Sort();
	pLibrary->Save();

	CPrefab::FreeAllData();	// free memory
	// re-enable prefab caching
	CPrefab::EnableCaching(TRUE);

	bCurLibraryModified = FALSE;
}


void CPrefabsDlg::AddToObjectList(CPrefab *pPrefab, int iItem, BOOL bReplace)
{
	if(iItem == -1)
		iItem = m_Objects.GetItemCount();
	if(bReplace)	// replace existing item
	{
		m_Objects.DeleteItem(iItem);
	}
	iItem = m_Objects.InsertItem(iItem, pPrefab->GetName(),
		pPrefab->GetType() == CPrefab::pftScript ? 1 : 0);
	m_Objects.SetItemData(iItem, pPrefab->GetID());

	bCurLibraryModified = TRUE;
}

BOOL CPrefabsDlg::HandleEditObjectPopup(UINT nID)
{
	switch(nID)
	{
	case id_EditObjectInfo:
		EditObjectInfo();
		break;
	case id_EditObjectData:
		EditObjectData();
		break;
	}
	return TRUE;
}

static BOOL IsValidFilename(LPCTSTR pszString)
// check for valid windows filename. no drive/dirs allowed.
{
	LPCTSTR p = pszString;
	while(p[0])
	{
		BYTE ch = BYTE(p[0]);
		++p;
		if(ch > 127 || isalpha(ch) || isdigit(ch) || 
			strchr(" $%`-_@~'!(){}^#&", ch))
			continue;
		// not one of those chars - not correct
		return FALSE;
	}

	return TRUE;
}

void CPrefabsDlg::EditObjectInfo()
{
	int iSel;
	CPrefab *pPrefab = GetCurrentObject(&iSel);

	CEditPrefabDlg dlg;
	dlg.m_strName = pPrefab->GetName();
	dlg.m_strDescript = pPrefab->GetNotes();

	dlg.SetRanges(500, -1);

	if(dlg.DoModal() == IDCANCEL)
		return;

	pPrefab->SetName(dlg.m_strName);
	pPrefab->SetNotes(dlg.m_strDescript);

	AddToObjectList(pPrefab, iSel, TRUE);
	bCurLibraryModified = TRUE;
}


void CPrefabsDlg::EditObjectData()
{
	// get application
	CHammer *pApp = (CHammer*) AfxGetApp();

	if(bCurLibraryModified)
	{
		CPrefabLibrary *pLibrary = GetCurrentLibrary();
		if(pLibrary)
			pLibrary->Save();
	}

	CMapDoc *pDoc = (CMapDoc*) pApp->pMapDocTemplate->OpenDocumentFile(NULL);
	pDoc->EditPrefab3D(GetCurrentObject()->GetID());
	EndDialog(IDOK);
}


void CPrefabsDlg::OnEditobject() 
{
	if(!GetCurrentObject())	// nothing
		return;

	// two stages - name/description OR data itself
	CMenu menu;
	menu.CreatePopupMenu();
	menu.AppendMenu(MF_STRING, id_EditObjectInfo, "Name and Description");
	menu.AppendMenu(MF_STRING, id_EditObjectData, "Prefab Data");

	// track menu
	CWnd *pButton = GetDlgItem(IDC_EDITOBJECT);
	CRect r;
	pButton->GetWindowRect(r);
	menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, r.left, r.bottom,
		this, NULL);
}

void CPrefabsDlg::OnRemoveobject() 
{
	// remove marked objectsss
	int iIndex = m_Objects.GetNextItem(-1, LVNI_SELECTED);
	BOOL bConfirmed = FALSE;
	CPrefabLibrary *pLibrary = GetCurrentLibrary();
	while(iIndex != -1)
	{
		CPrefab *pPrefab = CPrefab::FindID(m_Objects.GetItemData(iIndex));
		if(pPrefab)
		{
			// delete it
			if(!bConfirmed)
			{
				// do confirmation.
				if(AfxMessageBox("Are you sure you want to delete these "
					"items?", MB_YESNO) == IDNO)
					return;	// nope!
				m_Objects.SetRedraw(FALSE);	// no redraw while doing this
			}
			
			bConfirmed = TRUE;

			// remove from lib & delete
			pLibrary->Remove(pPrefab);
			delete pPrefab;

			// delete from list and shift index down so we keep processing 
			// correctly
			m_Objects.DeleteItem(iIndex--);
		}
		// get next item
		iIndex = m_Objects.GetNextItem(iIndex, LVNI_SELECTED);
	}

	// save library
	pLibrary->Save();

	m_Objects.SetRedraw(TRUE);	// redraw objects
	m_Objects.Invalidate();
}

void CPrefabsDlg::OnExportobject() 
{
	int iIndex = m_Objects.GetNextItem(-1, LVNI_SELECTED);
	while(iIndex != -1)
	{
		CPrefab *pPrefab = CPrefab::FindID(m_Objects.GetItemData(iIndex));
		if(pPrefab)
		{
			// export it
			CString strFilename;
			strFilename = pPrefab->GetName();
			CFileDialog dlg(FALSE, "map", strFilename, OFN_HIDEREADONLY | 
				OFN_OVERWRITEPROMPT, "Map files|*.map;*.rmf|", this);
			if(dlg.DoModal() == IDCANCEL)
				return;	// nevermind
			strFilename = dlg.GetPathName();

			int iPos = strFilename.Find('.');
			DWORD dwFlags = CPrefab::lsRMF;
			if(iPos != -1)
			{
				char *p = strFilename.GetBuffer(0);
				if(!strnicmp(p+iPos+1, "map", 3))
					dwFlags = CPrefab::lsMAP;
			}

			pPrefab->Save(strFilename, dwFlags);
		}
		// get next item
		iIndex = m_Objects.GetNextItem(iIndex, LVNI_SELECTED);
	}
}

void CPrefabsDlg::SetCurObject(int iItem)
{
	iCurObject = iItem;

	if(iCurObject == -1)
	{
		m_ObjectNotes.SetWindowText("");
		return;
	}

	// update data..
	CPrefab *pPrefab = CPrefab::FindID(m_Objects.GetItemData(iCurObject));
	Assert(pPrefab);

	m_ObjectNotes.SetWindowText(pPrefab->GetNotes());
}

void CPrefabsDlg::OnItemchangedObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;
	
	if(!(pNMListView->uChanged & LVIF_STATE))
		return;

	if(pNMListView->uNewState & LVIS_FOCUSED)
	{
		SetCurObject(pNMListView->iItem);
	}
}

//
// Library mgmt
//

CPrefabLibrary *CPrefabsDlg::GetCurrentLibrary(int *piSel)
{
	if(piSel)
		piSel[0] = -1;
	
	int iSel = m_Libraries.GetCurSel();
	if(iSel == CB_ERR)
		return NULL;
	CPrefabLibrary *pLibrary = CPrefabLibrary::FindID(
		m_Libraries.GetItemData(iSel));

	if(piSel)
		piSel[0] = iSel;
	return pLibrary;
}

CPrefab *CPrefabsDlg::GetCurrentObject(int *piSel)
{
	if(piSel)
		piSel[0] = -1;

	int iSel = iCurObject;
	if(iSel == -1)
		return NULL;
	CPrefab *pPrefab= CPrefab::FindID(m_Objects.GetItemData(iSel));

	if(piSel)
		piSel[0] = iSel;

	return pPrefab;
}

void CPrefabsDlg::OnSelchangeLibraries() 
{
	// get last library
	CPrefabLibrary *pLibrary = CPrefabLibrary::FindID(
		m_Libraries.GetItemData(iCurLibrary));

	// save its index
	if(bCurLibraryModified)
	{
		pLibrary->Save();
	}

	// update objects list
	m_Objects.DeleteAllItems();
	iCurLibrary = m_Libraries.GetCurSel();
	bCurLibraryModified = FALSE;
	pLibrary = GetCurrentLibrary();
	if(!pLibrary) return;

	// add objects to object list
	m_Objects.SetRedraw(FALSE);
	POSITION p = ENUM_START;
	CPrefab *pPrefab = pLibrary->EnumPrefabs(p);
	int iItem = 0;
	while(pPrefab)
	{
		AddToObjectList(pPrefab, iItem++);
		pPrefab = pLibrary->EnumPrefabs(p);
	}
	m_Objects.SetRedraw(TRUE);
	m_Objects.Invalidate();

	// set description window
	m_LibraryNotes.SetWindowText(pLibrary->GetNotes());
}

static int AskAboutInvalidFilename()
{
	return AfxMessageBox("That's not a valid name - some of the characters aren't\n"
			"acceptable. Try using a name with only A-Z, 0-9, space,\n"
			"and these characters: $%`-_@~'!(){}^#&", MB_OKCANCEL);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrefabsDlg::OnAddlibrary() 
{
Again:
	CEditPrefabDlg dlg;
	if(dlg.DoModal() == IDCANCEL)
		return;

	// check name
	if(!IsValidFilename(dlg.m_strName))
	{
		if(AskAboutInvalidFilename() == IDOK)
			goto Again;
		return;	// nevermind.
	}
	
	CPrefabLibraryRMF *pLibrary = new CPrefabLibraryRMF;

	pLibrary->SetName(dlg.m_strName);
	pLibrary->SetNotes(dlg.m_strDescript);

	// add to list
	int iIndex = m_Libraries.AddString(pLibrary->GetName());
	m_Libraries.SetItemData(iIndex, pLibrary->GetID());

	m_Libraries.SetCurSel(iIndex);
	OnSelchangeLibraries();	// to redraw description window
	bCurLibraryModified = TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrefabsDlg::OnEditlibrary() 
{
	// get selection
	int iSel;
	CPrefabLibrary *pLibrary = GetCurrentLibrary(&iSel);
	if(!pLibrary) return;

	CEditPrefabDlg dlg;
	dlg.m_strName = pLibrary->GetName();
	dlg.m_strDescript = pLibrary->GetNotes();

Again:
	if(dlg.DoModal() == IDCANCEL)
		return;

	// check name
	if(!IsValidFilename(dlg.m_strName))
	{
		if(AskAboutInvalidFilename() == IDOK)
			goto Again;
		return;	// nevermind.
	}

	pLibrary->SetName(dlg.m_strName);
	pLibrary->SetNotes(dlg.m_strDescript);

	// set in list
	m_Libraries.SetRedraw(FALSE);
	m_Libraries.DeleteString(iSel);
	int iIndex = m_Libraries.InsertString(iSel, pLibrary->GetName());
	m_Libraries.SetItemData(iIndex, pLibrary->GetID());
	m_Libraries.SetRedraw(TRUE);
	m_Libraries.Invalidate();

	m_Libraries.SetCurSel(iSel);
	OnSelchangeLibraries();	// to redraw description window
	bCurLibraryModified = TRUE;
}


void CPrefabsDlg::OnRemovelibrary() 
{
	// get cur library
	int iSel;
	CPrefabLibrary *pLibrary = GetCurrentLibrary(&iSel);
	if (pLibrary == NULL)
	{
		return;
	}

	if (AfxMessageBox("Are you sure you want to delete this library from your hard drive?", MB_YESNO) == IDYES)
	{
		pLibrary->DeleteFile();
		delete pLibrary;

		bCurLibraryModified = FALSE;

		m_Libraries.DeleteString(iSel);
		m_Libraries.SetCurSel(0);
		OnSelchangeLibraries();	// to redraw description window
	}
}


BOOL CPrefabsDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();

	SetCurObject(-1);

	// make imagelist
	PrefabImages.Create(IDB_PREFABS, 32, 1, RGB(0, 255, 255));
	PrefabImages.SetBkColor(m_Objects.GetBkColor());
	m_Objects.SetImageList(&PrefabImages, LVSIL_NORMAL);

	// add libraries to list
	POSITION p = ENUM_START;
	CPrefabLibrary *pLibrary = CPrefabLibrary::EnumLibraries(p);
	while(pLibrary)
	{
		int iIndex = m_Libraries.AddString(pLibrary->GetName());
		m_Libraries.SetItemData(iIndex, pLibrary->GetID());
		pLibrary = CPrefabLibrary::EnumLibraries(p);
	}

	iCurLibrary = 0;
	bCurLibraryModified = FALSE;
	m_Libraries.SetCurSel(0);
	OnSelchangeLibraries();
	
	return TRUE;
}


void CPrefabsDlg::OnEndlabeleditObjects(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	LV_ITEM &item = pDispInfo->item;
	
	*pResult = 0;

	if(item.pszText == NULL)
		return;

	CPrefab *pPrefab = CPrefab::FindID(m_Objects.GetItemData(item.iItem));
	pPrefab->SetName(item.pszText);
	m_Objects.SetItemText(item.iItem, 0, item.pszText);
	bCurLibraryModified = TRUE;
}

void CPrefabsDlg::OnClose() 
{
	// get library
	CPrefabLibrary *pLibrary = GetCurrentLibrary();

	// save it
	if(bCurLibraryModified && pLibrary)
	{
		pLibrary->Save();
	}
	
	CDialog::OnClose();
}