// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "std_afx.h"

#include "object_viewer.h"
#include "main_frame.h"
#include "snapshot_tool_dlg.h"
#include "choose_name.h"
#include "object_viewer.h"
//
#include "nel/misc/path.h"
#include "nel/misc/algo.h"
#include "nel/misc/file.h"
//
#include "nel/3d/u_scene.h"
#include "nel/3d/u_driver.h"
//
#include "nel/3d/driver.h"
#include "nel/3d/driver_user.h"
#include "nel/3d/shape_bank.h"
#include "nel/3d/camera.h"
#include "nel/3d/texture_multi_file.h"
#include "nel/3d/mesh_base.h"
#include "nel/3d/particle_system.h"
#include "nel/3d/particle_system_shape.h"
#include "nel/3d/particle_system_model.h"




#define NEL_OV_SNAPSHOT_TOOL_REGKEY "Software\\Nevrax\\nel\\object_viewer\\snapshot_dlg"


using namespace NLMISC;
using namespace NL3D;

/////////////////////////////////////////////////////////////////////////////
// CSnapshotToolDlg dialog

//****************************************************************************************
CSnapshotToolDlg::CSnapshotToolDlg(CObjectViewer *ov, CWnd* pParent /*=NULL*/)
	: CDialog(CSnapshotToolDlg::IDD, pParent)
{
	nlassert(ov);
	_ObjectViewer = ov;
	//{{AFX_DATA_INIT(CSnapshotToolDlg)
	m_OutputPath = _T("");
	m_InputPath = _T("");
	m_RecurseSubFolder = FALSE;
	m_OutputHeight = 100;		
	m_OutputWidth = 160;	
	m_Format = -1;
	m_OutputPathOption = -1;
	m_DumpTextureSets = FALSE;
	m_ViewBack = FALSE;
	m_ViewBottom = FALSE;
	m_ViewFront = FALSE;
	m_ViewLeft = FALSE;
	m_ViewRight = FALSE;
	m_ViewTop = FALSE;
	m_PostFixViewName = FALSE;
	//}}AFX_DATA_INIT
}

//****************************************************************************************
CSnapshotToolDlg::~CSnapshotToolDlg()
{	
}

//****************************************************************************************
void CSnapshotToolDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSnapshotToolDlg)
	DDX_Control(pDX, IDC_LOG, m_Log);
	DDX_Control(pDX, IDC_FILTERS, m_Filters);
	DDX_Text(pDX, IDC_OUTPUT_PATH, m_OutputPath);
	DDX_Text(pDX, IDC_INPUT_PATH, m_InputPath);
	DDX_Check(pDX, IDC_RECURSE_SUBFOLDER, m_RecurseSubFolder);	
	DDX_Text(pDX, IDC_HEIGHT, m_OutputHeight);
	DDV_MinMaxUInt(pDX, m_OutputHeight, 1, 1024);
	DDX_Text(pDX, IDC_WIDTH, m_OutputWidth);
	DDV_MinMaxUInt(pDX, m_OutputWidth, 1, 1024);	
	DDX_CBIndex(pDX, IDC_FORMAT, m_Format);
	DDX_CBIndex(pDX, IDC_OUTPUTPATH_OPTION, m_OutputPathOption);
	DDX_Check(pDX, IDC_DUMP_TEXTURE_SETS, m_DumpTextureSets);
	DDX_Check(pDX, IDC_VIEW_BACK, m_ViewBack);
	DDX_Check(pDX, IDC_VIEW_BOTTOM, m_ViewBottom);
	DDX_Check(pDX, IDC_VIEW_FRONT, m_ViewFront);
	DDX_Check(pDX, IDC_VIEW_LEFT, m_ViewLeft);
	DDX_Check(pDX, IDC_VIEW_RIGHT, m_ViewRight);
	DDX_Check(pDX, IDC_VIEW_TOP, m_ViewTop);
	DDX_Check(pDX, IDC_POSTFIX_VIEWNAME, m_PostFixViewName);
	//}}AFX_DATA_MAP
}

//****************************************************************************************
void CSnapshotToolDlg::stringFromRegistry(HKEY hKey, const char *name, CString &dest, const CString &defaultStr)
{	
	DWORD type;
	DWORD size;
	LONG result = RegQueryValueEx(hKey, name, NULL, &type, NULL, &size);
	if (type != REG_SZ || result != ERROR_SUCCESS || size == 0)
	{
		dest = defaultStr;
		return;
	}
	std::string tmpDest;
	tmpDest.resize(size);	
	result = RegQueryValueEx(hKey, name, NULL, &type, (unsigned char *) &tmpDest[0], &size);
	if (result != ERROR_SUCCESS)
	{
		dest = defaultStr;
		return;
	}
	dest = tmpDest.c_str();
}


//****************************************************************************************
template <class T, class U> void integralTypeFromRegistry(HKEY hKey, const char *name, T &dest, const U &defaultValue)
{	
	if (hKey == 0)
	{
		dest = defaultValue;
		return;
	}
	DWORD type;
	DWORD size;
	LONG result = RegQueryValueEx(hKey, name, NULL, &type, NULL, &size);
	if (type != REG_DWORD || result != ERROR_SUCCESS || size == 0)
	{
		dest = (T) defaultValue;
		return;
	}		
	DWORD value;
	result = RegQueryValueEx(hKey, name, NULL, &type, LPBYTE(&value), &size);
	if (result != ERROR_SUCCESS)
	{
		dest = defaultValue;
		return;
	}
	dest = (T) value;
}


//****************************************************************************************
void CSnapshotToolDlg::fromRegistry()
{
	HKEY hKey = 0;
	RegOpenKeyEx(HKEY_CURRENT_USER, NEL_OV_SNAPSHOT_TOOL_REGKEY, 0, KEY_READ, &hKey);					
	stringFromRegistry(hKey, "InputPath", m_InputPath, "");
	stringFromRegistry(hKey, "OutputPath", m_OutputPath, "");
	CString filters;
	stringFromRegistry(hKey, "Filters", filters, "*.shape");
	std::string stdFilters((LPCTSTR) filters);
	std::vector<std::string> filterList;
	NLMISC::splitString(stdFilters, ",", filterList);
	m_Filters.ResetContent();
	for (uint k = 0; k < filterList.size(); ++k)
	{
		m_Filters.AddString(filterList[k].c_str());
	}	
	
	integralTypeFromRegistry(hKey, "RecurseSubFolder", (int &) m_RecurseSubFolder, FALSE);
	integralTypeFromRegistry(hKey, "DumpTextureSets", (int &) m_DumpTextureSets, TRUE);
	integralTypeFromRegistry(hKey, "PostFixViewName", (int &) m_PostFixViewName, TRUE);
	integralTypeFromRegistry(hKey, "ViewBack", (int &) m_ViewBack, FALSE);
	integralTypeFromRegistry(hKey, "ViewBottom", (int &) m_ViewBottom, FALSE);
	integralTypeFromRegistry(hKey, "ViewFront", (int &) m_ViewFront, TRUE);
	integralTypeFromRegistry(hKey, "ViewLeft", (int &) m_ViewLeft, FALSE);
	integralTypeFromRegistry(hKey, "ViewRight", (int &) m_ViewRight, FALSE);
	integralTypeFromRegistry(hKey, "ViewTop", (int &) m_ViewTop, FALSE);
	integralTypeFromRegistry(hKey, "OutputWidth", m_OutputWidth, 128);
	integralTypeFromRegistry(hKey, "OutputHeight", m_OutputHeight, 128);	
	integralTypeFromRegistry(hKey, "Format", m_Format, 0);
	integralTypeFromRegistry(hKey, "OutputPathOption", m_OutputPathOption, 1);	
	UpdateData(FALSE);
	updateUIEnabledState();
}

//****************************************************************************************
void CSnapshotToolDlg::toRegistry()
{
	UpdateData();
	HKEY hKey;
	if (RegCreateKey(HKEY_CURRENT_USER, NEL_OV_SNAPSHOT_TOOL_REGKEY, &hKey)==ERROR_SUCCESS)
	{		
		RegSetValueEx(hKey, "InputPath", 0, REG_SZ, (unsigned char *) (LPCTSTR) m_InputPath, m_InputPath.GetLength() + 1);
		RegSetValueEx(hKey, "OutputPath", 0, REG_SZ, (unsigned char *) (LPCTSTR) m_OutputPath, m_OutputPath.GetLength() + 1);
		CString filters;
		for (uint k = 0; k < (uint) m_Filters.GetCount(); ++k)
		{
			if (k!=0) filters += ",";
			CString filter;
			m_Filters.GetText(k, filter);
			filters += filter;			
		}
		RegSetValueEx(hKey, "Filters", 0, REG_SZ, (unsigned char *) (LPCTSTR) filters, filters.GetLength() + 1);
		DWORD recurseSubFolder = m_RecurseSubFolder;
		DWORD dumpTextureSets = m_DumpTextureSets;
		DWORD views [] = 
		{ 
			m_ViewBack,
			m_ViewBottom,
			m_ViewFront,
			m_ViewLeft,
			m_ViewRight,
			m_ViewTop
		};
		DWORD width = (DWORD) m_OutputWidth;
		DWORD height = (DWORD) m_OutputHeight;		
		DWORD format = m_Format;
		DWORD outputPathOption = m_OutputPathOption;
		DWORD postFixViewName = m_PostFixViewName;
		integralTypeFromRegistry(hKey, "PostFixViewName", (int &) m_PostFixViewName, TRUE);
		RegSetValueEx(hKey, "ViewBack", 0, REG_DWORD, (const BYTE *) &m_ViewBack, sizeof(DWORD));
		RegSetValueEx(hKey, "ViewBottom", 0, REG_DWORD, (const BYTE *) &m_ViewBottom, sizeof(DWORD));
		RegSetValueEx(hKey, "ViewFront", 0, REG_DWORD, (const BYTE *) &m_ViewFront, sizeof(DWORD));
		RegSetValueEx(hKey, "ViewLeft", 0, REG_DWORD, (const BYTE *) &m_ViewLeft, sizeof(DWORD));
		RegSetValueEx(hKey, "ViewRight", 0, REG_DWORD, (const BYTE *) &m_ViewRight, sizeof(DWORD));
		RegSetValueEx(hKey, "ViewTop", 0, REG_DWORD, (const BYTE *) &m_ViewTop, sizeof(DWORD));
		RegSetValueEx(hKey, "RecurseSubFolder", 0, REG_DWORD, (const BYTE *) &recurseSubFolder, sizeof(DWORD));
		RegSetValueEx(hKey, "DumpTextureSets", 0, REG_DWORD, (const BYTE *) &dumpTextureSets, sizeof(DWORD));
		RegSetValueEx(hKey, "OutputWidth", 0, REG_DWORD, (const BYTE *) &width, sizeof(DWORD));
		RegSetValueEx(hKey, "OutputHeight", 0, REG_DWORD, (const BYTE *) &height, sizeof(DWORD));		
		RegSetValueEx(hKey, "Format", 0, REG_DWORD, (const BYTE *) &format, sizeof(DWORD));
		RegSetValueEx(hKey, "OutputPathOption", 0, REG_DWORD, (const BYTE *) &outputPathOption, sizeof(DWORD));
	}
}


BEGIN_MESSAGE_MAP(CSnapshotToolDlg, CDialog)
	//{{AFX_MSG_MAP(CSnapshotToolDlg)
	ON_BN_CLICKED(IDC_BROWSE_INPUT_PATH, OnBrowseInputPath)
	ON_BN_CLICKED(IDC_BROWSE_OUTPUT_PATH, OnBrowseOutputPath)
	ON_BN_CLICKED(IDC_GO, OnGo)
	ON_BN_CLICKED(IDC_ADD_FILTER, OnAddFilter)
	ON_BN_CLICKED(IDC_REMOVE_FILTER, OnRemoveFilter)
	ON_BN_CLICKED(IDC_EDIT_FILTER, OnEditFilter)
	ON_WM_CLOSE()
	ON_EN_CHANGE(IDC_WIDTH, OnChangeWidth)
	ON_EN_KILLFOCUS(IDC_HEIGHT, OnKillfocusHeight)
	ON_EN_KILLFOCUS(IDC_WIDTH, OnKillfocusWidth)
	ON_CBN_SELCHANGE(IDC_OUTPUTPATH_OPTION, OnSelchangeOutputpathOption)
	ON_CBN_SELCHANGE(IDC_FORMAT, OnSelchangeFormat)
	ON_BN_CLICKED(IDC_CLOSE, OnCloseButton)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_STOP, OnStopSnapshots)
	ON_BN_CLICKED(IDC_VIEW_FRONT, OnViewFront)
	ON_BN_CLICKED(IDC_VIEW_LEFT, OnViewLeft)
	ON_BN_CLICKED(IDC_VIEW_RIGHT, OnViewRight)
	ON_BN_CLICKED(IDC_VIEW_TOP, OnViewTop)
	ON_BN_CLICKED(IDC_VIEW_ALL, OnViewAll)
	ON_BN_CLICKED(IDC_VIEW_NONE, OnViewNone)
	ON_BN_CLICKED(IDC_POSTFIX_VIEWNAME, OnPostFixViewName)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSnapshotToolDlg message handlers


//****************************************************************************************
BOOL CSnapshotToolDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	fromRegistry();	
	SetTimer(1, 10, NULL);
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

//****************************************************************************************
void CSnapshotToolDlg::OnClose() 
{
	toRegistry();
	CDialog::OnClose();
}


//****************************************************************************************
void CSnapshotToolDlg::OnBrowseInputPath() 
{
	browseFolder(getStrRsc(IDS_CHOOSE_SNAPSHOT_INPUT_PATH), m_InputPath, this->m_hWnd);
	UpdateData(FALSE);
	toRegistry();
}

//****************************************************************************************
void CSnapshotToolDlg::OnBrowseOutputPath() 
{
	browseFolder(getStrRsc(IDS_CHOOSE_SNAPSHOT_OUTPUT_PATH), m_OutputPath, this->m_hWnd);
	UpdateData(FALSE);
	toRegistry();
}


//****************************************************************************************
void CSnapshotToolDlg::OnAddFilter() 
{
	CChooseName cn("*.shape", this);
	if (cn.DoModal() == IDOK && !cn.m_Name.IsEmpty())
	{
		m_Filters.AddString(cn.m_Name);
	}
	toRegistry();
}

//****************************************************************************************
void CSnapshotToolDlg::OnRemoveFilter() 
{
	if (m_Filters.GetCurSel() != LB_ERR)
	{
		m_Filters.DeleteString(m_Filters.GetCurSel());
	}
}


//****************************************************************************************
void CSnapshotToolDlg::OnEditFilter() 
{
	if (m_Filters.GetCurSel() != LB_ERR)
	{
		CString current;
		m_Filters.GetText(m_Filters.GetCurSel(), current);
		CChooseName cn((LPCTSTR) current, this);
		if (cn.DoModal() == IDOK && !cn.m_Name.IsEmpty())
		{
			int curSel = m_Filters.GetCurSel();
			m_Filters.DeleteString(m_Filters.GetCurSel());
			m_Filters.InsertString(curSel, cn.m_Name);
			m_Filters.SetCurSel(curSel);			
		}
	}
	toRegistry();
}


//****************************************************************************************
uint CSnapshotToolDlg::getSelectedViewCount() 
{
	UpdateData();
	return (m_ViewBack ? 1 : 0) +
			(m_ViewBottom ? 1 : 0) +
			(m_ViewFront ? 1 : 0) + 
			(m_ViewLeft ? 1 : 0) +
			(m_ViewRight ? 1 : 0) +
			(m_ViewTop ? 1 : 0);
}

//****************************************************************************************
void CSnapshotToolDlg::updateUIEnabledState() 
{
	BOOL enabled = _FilteredFiles.empty() ? TRUE : FALSE;
	m_Filters.EnableWindow(enabled);		
    GetDlgItem(IDC_INPUT_PATH)->EnableWindow(enabled);
    GetDlgItem(IDC_BROWSE_INPUT_PATH)->EnableWindow(enabled);
    GetDlgItem(IDC_OUTPUT_PATH)->EnableWindow(enabled  && m_OutputPathOption == OutputPath_Custom);
    GetDlgItem(IDC_BROWSE_OUTPUT_PATH)->EnableWindow(enabled);
    GetDlgItem(IDC_RECURSE_SUBFOLDER)->EnableWindow(enabled);
    GetDlgItem(IDC_OUTPUTPATH_OPTION)->EnableWindow(enabled);
    GetDlgItem(IDC_FILTERS)->EnableWindow(enabled);
    GetDlgItem(IDC_ADD_FILTER)->EnableWindow(enabled);
    GetDlgItem(IDC_REMOVE_FILTER)->EnableWindow(enabled);
    GetDlgItem(IDC_EDIT_FILTER)->EnableWindow(enabled);
    GetDlgItem(IDC_WIDTH)->EnableWindow(enabled);
    GetDlgItem(IDC_HEIGHT)->EnableWindow(enabled);    
    GetDlgItem(IDC_FORMAT)->EnableWindow(enabled);
    GetDlgItem(IDC_GO)->EnableWindow(enabled);
	GetDlgItem(IDC_DUMP_TEXTURE_SETS)->EnableWindow(enabled);
    GetDlgItem(IDC_STOP)->EnableWindow(!enabled);				
	GetDlgItem(IDC_POSTFIX_VIEWNAME)->EnableWindow((enabled && getSelectedViewCount() == 1) ? TRUE : FALSE);
    //GetDlgItem(IDC_CLOSE)->EnaleWindow(enabled);

}

//****************************************************************************************
void CSnapshotToolDlg::OnGo() 
{	
	UpdateData();
	if (getSelectedViewCount() == 0)
	{
		MessageBox(getStrRsc(IDS_SNAPSHOT_NO_VIEW_SELECTED), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
		return;
	}
	if (m_InputPath.IsEmpty())
	{
		MessageBox(getStrRsc(IDS_SNAPSHOT_EMPTY_INPUT_PATH), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
		return;
	}
	if (!NLMISC::CFile::isDirectory(LPCTSTR(m_InputPath)))
	{
		MessageBox(getStrRsc(IDS_SNAPSHOT_EMPTY_INPUT_PATH_NOT_FOUND), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
		return;
	}
	if (m_OutputPathOption == OutputPath_Custom && m_OutputPath.IsEmpty())
	{
		MessageBox(getStrRsc(IDS_SNAPSHOT_EMPTY_OUTPUT_PATH), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
		return;
	}
	if (m_OutputPathOption == OutputPath_Custom && !NLMISC::CFile::isDirectory(LPCTSTR(m_OutputPath)))
	{
		if (MessageBox(getStrRsc(IDS_SNAPSHOT_CREATE_OUTPUT_DIRECTORY), getStrRsc(IDS_OBJECT_VIEWER), MB_OKCANCEL) != IDOK)
		{
			return;
		}
		if(!NLMISC::CFile::createDirectoryTree(LPCTSTR(m_OutputPath)))
		{
			MessageBox(getStrRsc(IDS_SNAPSHOT_OUTPUT_PATH_CREATION_FAILED), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
			return;
		}
	}


	// make sure that the screen can contains the window client area
	RECT desktopSize;
	::GetClientRect(::GetDesktopWindow(), &desktopSize);
	if (desktopSize.right < LONG(m_OutputWidth) ||
		desktopSize.bottom < LONG(m_OutputHeight))
	{
		MessageBox(getStrRsc(IDS_DESKTOP_TOO_SMALL_FOR_SNAPSHOT), getStrRsc(IDS_OBJECT_VIEWER), MB_ICONEXCLAMATION);
		return;
	}
	
	m_Log.ResetContent();
	m_Log.AddString(getStrRsc(IDS_GETTING_PATH_CONTENT));
	std::vector<std::string> files;
	CPath::getPathContent((LPCTSTR) m_InputPath, m_RecurseSubFolder == TRUE, false, true, files);
	if (files.empty())
	{
		m_Log.AddString(getStrRsc(IDS_SNAPSHOT_NO_FILES_FOUND));
		return;
	}
	_FilteredFiles.clear();
	for (uint k = 0; k < files.size(); ++k)
	{
		for (uint l = 0; l < uint(m_Filters.GetCount()); ++l)
		{
			CString wildCard;
			m_Filters.GetText(l, wildCard);
			wildCard.MakeLower();
			if (testWildCard(toLower(NLMISC::CFile::getFilename(files[k])).c_str(), (LPCTSTR) wildCard))
			{
				_FilteredFiles.push_back(files[k]);
				break;
			}
		}
	}
	if (_FilteredFiles.empty())
	{
		m_Log.AddString(getStrRsc(IDS_SNAPSHOT_NO_FILTER_MATCH_FOUND));
		return;
	}
	m_Log.AddString(toString("%d", (int) _FilteredFiles.size()).c_str() + getStrRsc(IDS_SNAPSHOT_FILES_TO_PROCESS));			
	updateUIEnabledState();
}

//****************************************************************************************
void CSnapshotToolDlg::OnChangeWidth() 
{
	// TODO: If this is a RICHEDIT control, the control will not
	// send this notification unless you override the CDialog::OnInitDialog()
	// function and call CRichEditCtrl().SetEventMask()
	// with the ENM_CHANGE flag ORed into the mask.
	
	// TODO: Add your control notification handler code here

}

//****************************************************************************************
void CSnapshotToolDlg::OnKillfocusHeight() 
{
	toRegistry();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnKillfocusWidth() 
{
	toRegistry();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnSelchangeOutputpathOption() 
{
	toRegistry();
	updateUIEnabledState();
}


//****************************************************************************************
void CSnapshotToolDlg::OnSelchangeFormat() 
{
	toRegistry();	
}


//****************************************************************************************
void CSnapshotToolDlg::OnCloseButton() 
{
	toRegistry();
	SendMessage(WM_CLOSE);
}

//****************************************************************************************
void CSnapshotToolDlg::setCamFromView(uint view, CCamera *cam, const CAABBox &bbox) 
{
	static const CVector camBackDir[] = 
	{
		-CVector::J,
		CVector::I,
		-CVector::I,
		CVector::K,
		-CVector::K,		
		CVector::J
	};
	const float viewDistScale = 3.f;
	CVector halfSize = bbox.getHalfSize();
	if (view == SnapshotAngle_Top) // top
	{
		CMatrix camMat;
		camMat.setRot(CVector::I, -CVector::K, CVector::J);
		camMat.setPos(bbox.getCenter() + viewDistScale * CVector::K);
		cam->setMatrix(camMat);
	}
	else if (view == SnapshotAngle_Bottom)
	{
		CMatrix camMat;
		camMat.setRot(CVector::I, CVector::K, -CVector::J);
		camMat.setPos(bbox.getCenter() - viewDistScale * CVector::K);
		cam->setMatrix(camMat);
	}
	else
	{
		// standard lookat for other directions
		cam->lookAt(bbox.getCenter() + viewDistScale * maxof(halfSize.x, halfSize.y, halfSize.z) * camBackDir[view], bbox.getCenter());
	}
}

//****************************************************************************************
std::string CSnapshotToolDlg::viewToString(uint view)
{
	switch(view)
	{
	case SnapshotAngle_Front: return "front";
	case SnapshotAngle_Right: return "right";
	case SnapshotAngle_Left: return "left";
	case SnapshotAngle_Top: return "top";
	case SnapshotAngle_Bottom: return "bottom";
	case SnapshotAngle_Back: return "back";
	}
	nlassert(0);
	return "";
}

//****************************************************************************************
void CSnapshotToolDlg::OnTimer(UINT_PTR nIDEvent) 
{
	if (_FilteredFiles.empty()) return;	
	if (nIDEvent == 1)
	{
		// resize the window if needed
		HWND wnd = (HWND) NL3D::CNELU::Driver->getDisplay();		
		CScene		 scene(true /* small scene */);

		scene.initDefaultRoots();		
		scene.setDriver(CNELU::Driver);		
		scene.initQuadGridClipManager();
		NL3D::CViewport vp;
		vp.init(0.f, 0.f, 1.f, 1.f);
		scene.setViewport(vp);
		scene.enableLightingSystem(true);
		scene.setAmbientGlobal(CRGBA::White);

		CShapeBank   sb;
		scene.setShapeBank(&sb);
		CCamera *cam= safe_cast<CCamera *>(scene.createModel(CameraId));
		scene.setCam(cam);
		cam->setFrustum(0.1f, 0.1f, 0.1f, 5000.f);				
				

		try
		{
			CShapeStream ss;
			m_Log.AddString(_FilteredFiles[0].c_str());
			CIFile stream(_FilteredFiles[0]);
			ss.serial(stream);
			nlassert(ss.getShapePointer());
			CAABBox bbox;
			ss.getShapePointer()->getAABBox(bbox);
			sb.add("shape", ss.getShapePointer());
			CTransformShape	*model = scene.createInstance("shape");						
			CMatrix initTM = model->getMatrix();
			model->setTransformMode(CTransform::DirectMatrix);
			initTM.setPos(CVector::Null);
			model->setMatrix(initTM);
			CVector newBBoxCenter = initTM * bbox.getCenter();
			CVector newBBoxHalfSize = initTM.mulVector(bbox.getHalfSize());
			bbox.setCenter(newBBoxCenter);
			bbox.setHalfSize(newBBoxHalfSize);
			if (model)
			{				
				CMeshBaseInstance *mbi = dynamic_cast<CMeshBaseInstance *>(model);
				uint textureSetCount = 1;
				CMeshBase *mb = dynamic_cast<CMeshBase *>((IShape *) model->Shape);
				CMesh	  *mesh = dynamic_cast<CMesh *>((IShape *) model->Shape);
				if (m_DumpTextureSets && mbi)
				{					
					if (mb)
					{
						const uint numMat = mb->getNbMaterial();	
						// see which material are selectable
						for(uint k = 0; k < numMat; ++k)
						{
							CMaterial &mat = mb->getMaterial(k);
							for(uint l = 0; l < IDRV_MAT_MAXTEXTURES; ++l)
							{
								CTextureMultiFile *tmf = dynamic_cast<CTextureMultiFile *>(mat.getTexture(l));
								if (tmf)
								{
									textureSetCount = std::max(textureSetCount, (uint) tmf->getNumFileName());
								}
							}
						}
					}
				}		
				// for particle systems :  simulate until half duration, then capture bbox
				CParticleSystemModel *psm = dynamic_cast<CParticleSystemModel *>(model);
				if (psm)
				{
					psm->forceInstanciate();
					CParticleSystem *ps = psm->getPS();
					if (ps)
					{
						float duration = ps->evalDuration();
						float deltaT = .5f * ps->getTimeTheshold();
						for (float date = 0.f; date < duration * 0.5f; date += deltaT) 
						{
							ps->setSysMat(&CMatrix::Identity);
							ps->step(CParticleSystem::Anim, deltaT, *safe_cast<CParticleSystemShape *>((IShape *) psm->Shape), *psm);
						}
						ps->computeBBox(bbox);
					}
				}

				cam->setTransformMode(ITransformable::DirectMatrix);				
				BOOL views[] = 
				{ 
					m_ViewFront,
					m_ViewRight,
					m_ViewLeft,
					m_ViewTop,
					m_ViewBottom,
					m_ViewBack
				};
				for (uint viewIndex = 0; viewIndex < 6; ++viewIndex)
				{
					if (!views[viewIndex]) continue;
					setCamFromView(viewIndex, cam, bbox);					
					for (uint textureSet = 0; textureSet < textureSetCount; ++textureSet)
					{
						if (mbi)
						{
							mbi->selectTextureSet(textureSet);
						}

						CNELU::Driver->clear2D(_ObjectViewer->getBackGroundColor());
						CNELU::Driver->clearZBuffer();
						scene.render();
						NLMISC::CBitmap snapshot;
						CNELU::Driver->getBuffer(snapshot);
						if (snapshot.getWidth() == 0 || snapshot.getHeight() == 0)
						{
							m_Log.AddString(getStrRsc(IDS_SNAPSHOT_NO_CONTENT_RETRIEVED));
						}
						else
						{
							snapshot.resample(m_OutputWidth, m_OutputHeight);
							size_t lastPoint = _FilteredFiles[0].find_last_of('.');
							std::string outputFilename = _FilteredFiles[0].substr(0, lastPoint);
							if (textureSetCount > 1)
							{
								outputFilename += toString("_%u", (unsigned int) textureSet);
							}
							if (getSelectedViewCount() != 1 || m_PostFixViewName)
							{
								outputFilename += "_" + viewToString(viewIndex);
							}
							outputFilename += (m_Format == OutputFormat_Tga ? ".tga" : ".jpg");
							switch(m_OutputPathOption)
							{
								case OutputPath_Custom: // custom output path
									outputFilename = LPCTSTR(m_OutputPath) + std::string("\\") + NLMISC::CFile::getFilename(outputFilename);
								break;
								case OutputPath_SameAsInput: // Input path
									outputFilename = LPCTSTR(m_InputPath) + std::string("\\") + NLMISC::CFile::getFilename(outputFilename);
								break;
								case OutputPath_CurrShapeDirectory: // current path
									// no op
								break;
							}
							COFile output(outputFilename);
							if (m_Format == OutputFormat_Tga)
							{
								snapshot.writeTGA(output);
							}
							else
							{
								snapshot.writeJPG(output);
							}						
						}
						CNELU::Driver->swapBuffers();
					}
				}
				scene.deleteInstance(model);
			}
			sb.reset();
		}
		catch(std::exception &e)
		{
			nlwarning(e.what());
			
		}				
		_FilteredFiles.pop_front();
		if (_FilteredFiles.empty())
		{
			m_Log.AddString(getStrRsc(IDS_SNAPSHOT_FINISHED));
			updateUIEnabledState();
		}
	}
	CDialog::OnTimer(nIDEvent);
}

//****************************************************************************************
void CSnapshotToolDlg::OnStopSnapshots() 
{
	_FilteredFiles.clear();
	updateUIEnabledState();
}


//****************************************************************************************
void CSnapshotToolDlg::OnViewFront() 
{
	toRegistry();
	updateUIEnabledState();
}

//****************************************************************************************
void CSnapshotToolDlg::OnViewLeft() 
{
	toRegistry();
	updateUIEnabledState();
}

//****************************************************************************************
void CSnapshotToolDlg::OnViewRight() 
{
	toRegistry();
	updateUIEnabledState();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnViewTop() 
{
	toRegistry();
	updateUIEnabledState();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnViewAll() 
{
	m_ViewBack = TRUE;
	m_ViewBottom = TRUE;
	m_ViewFront = TRUE;
	m_ViewLeft = TRUE;
	m_ViewRight = TRUE;
	m_ViewTop = TRUE;
	UpdateData(FALSE);
	toRegistry();
	updateUIEnabledState();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnViewNone() 
{
	m_ViewBack = FALSE;
	m_ViewBottom = FALSE;
	m_ViewFront = FALSE;
	m_ViewLeft = FALSE;
	m_ViewRight = FALSE;
	m_ViewTop = FALSE;
	UpdateData(FALSE);
	toRegistry();
	updateUIEnabledState();	
}

//****************************************************************************************
void CSnapshotToolDlg::OnPostFixViewName() 
{
	toRegistry();
}