// 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 "emitter_dlg.h"
#include "direction_attr.h"
#include "particle_tree_ctrl.h"
#include "particle_dlg.h"

#include "nel/3d/particle_system.h"



/////////////////////////////////////////////////////////////////////////////
// CEmitterDlg dialog


CEmitterDlg::CEmitterDlg(CParticleWorkspace::CNode *ownerNode, NL3D::CPSEmitter *emitter, CParticleDlg *particleDlg)
	  : _Node(ownerNode),
		_Emitter(emitter),
	    _PeriodDlg(NULL),
		_GenNbDlg(NULL),
	    _StrenghtModulateDlg(NULL),
	    _SpeedInheritanceFactorDlg(NULL),
	    _ParticleDlg(particleDlg)
{
	nlassert(_Emitter);
	nlassert(_ParticleDlg);
	//{{AFX_DATA_INIT(CEmitterDlg)
	m_ConsistentEmission = _Emitter->isConsistentEmissionEnabled();
	m_BypassAutoLOD = FALSE;
	//}}AFX_DATA_INIT
}

CEmitterDlg::~CEmitterDlg()
{
	_PeriodDlg->DestroyWindow();
	_GenNbDlg->DestroyWindow();
	_StrenghtModulateDlg->DestroyWindow();
	_SpeedInheritanceFactorDlg->DestroyWindow();
	_DelayedEmissionDlg->DestroyWindow();
	_MaxEmissionCountDlg->DestroyWindow();

	delete _PeriodDlg;
	delete _GenNbDlg;
	delete _StrenghtModulateDlg;
	delete _SpeedInheritanceFactorDlg;
	delete _DelayedEmissionDlg;
	delete _MaxEmissionCountDlg;
}


void CEmitterDlg::init(CWnd* pParent)
{
	Create(IDD_EMITTER_DIALOG, pParent);	
	// fill the emitted type combo box with all the types of located	
	initEmittedType();	
	m_EmissionTypeCtrl.SetCurSel((int) _Emitter->getEmissionType() );
	ShowWindow(SW_SHOW); 
	UpdateData(FALSE);
}

void CEmitterDlg::initEmittedType()
{	
	m_EmittedTypeCtrl.ResetContent();
	NL3D::CParticleSystem *ps = _Emitter->getOwner()->getOwner();
	uint nbLocated = ps->getNbProcess(); 
	m_EmittedTypeCtrl.InitStorage(nbLocated, 16);	
	for (uint k = 0; k < nbLocated; ++k)
	{
		NL3D::CPSLocated *loc = dynamic_cast<NL3D::CPSLocated *>(ps->getProcess(k));
		if (loc) // is this a located
		{
			m_EmittedTypeCtrl.AddString(loc->getName().c_str());
			_LocatedList.push_back(loc);			
			if (loc == _Emitter->getEmittedType())
			{
				m_EmittedTypeCtrl.SetCurSel(k);
			}
		}
	}
}

void CEmitterDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CEmitterDlg)
	DDX_Control(pDX, IDC_DIRECTION_MODE, m_DirectionModeCtrl);
	DDX_Control(pDX, IDC_TYPE_OF_EMISSION, m_EmissionTypeCtrl);
	DDX_Control(pDX, IDC_EMITTED_TYPE, m_EmittedTypeCtrl);	
	DDX_Check(pDX, IDC_CONSISTENT_EMISSION, m_ConsistentEmission);
	DDX_Check(pDX, IDC_BYPASS_AUTOLOD, m_BypassAutoLOD);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CEmitterDlg, CDialog)
	//{{AFX_MSG_MAP(CEmitterDlg)
	ON_CBN_SELCHANGE(IDC_EMITTED_TYPE, OnSelchangeEmittedType)
	ON_CBN_SELCHANGE(IDC_TYPE_OF_EMISSION, OnSelchangeTypeOfEmission)	
	ON_BN_CLICKED(IDC_CONSISTENT_EMISSION, OnConsistentEmission)
	ON_BN_CLICKED(IDC_BYPASS_AUTOLOD, OnBypassAutoLOD)
	ON_CBN_SELCHANGE(IDC_DIRECTION_MODE, OnSelchangeDirectionMode)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEmitterDlg message handlers

void CEmitterDlg::OnSelchangeEmittedType() 
{
	UpdateData();
	uint k = m_EmittedTypeCtrl.GetCurSel();
	if (!_Emitter->setEmittedType(_LocatedList[k]))
	{
		if (_Emitter->getOwner()->getOwner()->getBehaviourType() == NL3D::CParticleSystem::SpellFX || _Emitter->getOwner()->getOwner()->getBypassMaxNumIntegrationSteps())
		{		
			MessageBox("Can't perform operation : the system is flagged with 'No max nb steps' or uses the preset 'Spell FX', and thus, should have a finite duration. This operation create a loop in the system, and so is forbidden.", "Error", MB_ICONEXCLAMATION);
		}
		else
		{
			MessageBox("Loops with emitters are forbidden.", "Error", MB_ICONEXCLAMATION);
		}
		initEmittedType();
	}	
	_ParticleDlg->StartStopDlg->resetAutoCount(_Node);
	updateModifiedFlag();
}

void CEmitterDlg::OnSelchangeTypeOfEmission() 
{
	UpdateData();
	if (!_Emitter->setEmissionType((NL3D::CPSEmitter::TEmissionType) m_EmissionTypeCtrl.GetCurSel()))
	{
		CString mess;
		mess.LoadString(IDS_PS_NO_FINITE_DURATION);
		CString errorStr;
		errorStr.LoadString(IDS_ERROR);
		MessageBox((LPCTSTR) mess, (LPCTSTR) errorStr, MB_ICONEXCLAMATION);
		m_EmissionTypeCtrl.SetCurSel((int) _Emitter->getEmissionType());		
	}

	updatePeriodDlg();
	_ParticleDlg->StartStopDlg->resetAutoCount(_Node);
	updateModifiedFlag();
}


void CEmitterDlg::updatePeriodDlg(void)
{
	BOOL bEnable = _Emitter->getEmissionType() == NL3D::CPSEmitter::regular;
	_PeriodDlg->EnableWindow(bEnable);
	_DelayedEmissionDlg->EnableWindow(bEnable);
	_MaxEmissionCountDlg->EnableWindow(bEnable);
}

BOOL CEmitterDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	RECT r;

	 GetDlgItem(IDC_SPEED_INHERITANCE_FACTOR_FRAME)->GetWindowRect(&r);
	 ScreenToClient(&r);
	_SpeedInheritanceFactorDlg = new CEditableRangeFloat("SPEED_INHERITANCE_FACTOR", _Node, -1.f, 1.f);
	_SpeedInheritanceFactorWrapper.E = _Emitter;
	_SpeedInheritanceFactorDlg->setWrapper(&_SpeedInheritanceFactorWrapper);
	_SpeedInheritanceFactorDlg->init(r.left, r.top, this);

	 GetDlgItem(IDC_DELAYED_EMISSION_FRAME)->GetWindowRect(&r);
	 ScreenToClient(&r);
	_DelayedEmissionDlg = new CEditableRangeFloat("DELAYED_EMISSION", _Node, 0.f, 10.f);
	_DelayedEmissionDlg->enableLowerBound(0.f, false);
	_DelayedEmissionWrapper.E = _Emitter;
	_DelayedEmissionWrapper.Node = _Node;
	_DelayedEmissionWrapper.SSPS = _ParticleDlg->StartStopDlg;
	_DelayedEmissionDlg->setWrapper(&_DelayedEmissionWrapper);
	_DelayedEmissionDlg->init(r.left, r.top, this);

	GetDlgItem(IDC_MAX_EMISSION_COUNT_FRAME)->GetWindowRect(&r);
	 ScreenToClient(&r);
	_MaxEmissionCountDlg = new CEditableRangeUInt("MAX_EMISSION_COUNT", _Node, 0, 100);
	_MaxEmissionCountDlg->enableUpperBound(256, false);
	_MaxEmissionCountWrapper.E = _Emitter;
	_MaxEmissionCountWrapper.Node = _Node;
	_MaxEmissionCountWrapper.SSPS = _ParticleDlg->StartStopDlg;
	_MaxEmissionCountWrapper.HWnd = (HWND) (*this);
	_MaxEmissionCountDlg->setWrapper(&_MaxEmissionCountWrapper);
	_MaxEmissionCountDlg->init(r.left, r.top, this);
	_MaxEmissionCountWrapper.MaxEmissionCountDlg = _MaxEmissionCountDlg;



	uint posX = 13;
	uint posY = r.bottom + 5;	

	// setup the dialog for the period of emission edition

	_PeriodDlg = new CAttribDlgFloat("EMISSION_PERIOD", _Node, 0.f, 2.f);	
	_PeriodWrapper.E = _Emitter;
	_PeriodWrapper.Node = _Node;
	_PeriodWrapper.SSPS = _ParticleDlg->StartStopDlg;
	_PeriodDlg->setWrapper(&_PeriodWrapper);
	_PeriodDlg->setSchemeWrapper(&_PeriodWrapper);
	HBITMAP bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_PERIOD));
	_PeriodDlg->init(bmh, posX, posY, this);
	posY += 120;

	// setup the dialog that helps tuning the number of particle being emitted at a time

	_GenNbDlg = new CAttribDlgUInt("EMISSION_GEN_NB",  _Node, 1, 11);
	_GenNbWrapper.E = _Emitter;
	_GenNbWrapper.Node = _Node;
	_GenNbWrapper.SSPS = _ParticleDlg->StartStopDlg;
	_GenNbDlg->setWrapper(&_GenNbWrapper);
	_GenNbDlg->setSchemeWrapper(&_GenNbWrapper);
	bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_QUANTITY));
	_GenNbDlg->init(bmh, posX, posY, this);
	posY += 120;

	if (dynamic_cast<NL3D::CPSModulatedEmitter *>(_Emitter))
	{
		_StrenghtModulateDlg = new CAttribDlgFloat("EMISSION_GEN_NB",  _Node, 1, 11);
		_ModulatedStrenghtWrapper.E = dynamic_cast<NL3D::CPSModulatedEmitter *>(_Emitter);
		_StrenghtModulateDlg->setWrapper(&_ModulatedStrenghtWrapper);
		_StrenghtModulateDlg->setSchemeWrapper(&_ModulatedStrenghtWrapper);
		bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_MODULATE_STRENGHT));
		_StrenghtModulateDlg->init(bmh, posX, posY, this);
		posY += 120;
	}

	// deals with emitters that have a direction
	if (dynamic_cast<NL3D::CPSDirection *>(_Emitter))
	{
		CDirectionAttr *da = new CDirectionAttr(std::string("DIRECTION"));
		pushWnd(da);		
		_DirectionWrapper.E = dynamic_cast<NL3D::CPSDirection *>(_Emitter);
		da->setWrapper(&_DirectionWrapper);
		da->setDirectionWrapper(dynamic_cast<NL3D::CPSDirection *>(_Emitter));
		da->init(posX, posY, this);
		da->GetClientRect(&r);
		posY += r.bottom;
	}

	// radius  for conic emitter
	if (dynamic_cast<NL3D::CPSEmitterConic *>(_Emitter))
	{
		CEditableRangeFloat *ecr = new CEditableRangeFloat(std::string("CONIC EMITTER RADIUS"), _Node, 0.1f, 2.1f);
		pushWnd(ecr);
		_ConicEmitterRadiusWrapper.E = dynamic_cast<NL3D::CPSEmitterConic *>(_Emitter);
		ecr->setWrapper(&_ConicEmitterRadiusWrapper);
		ecr->init(posX + 80, posY, this);

		CStatic *s = new CStatic;
		pushWnd(s);
		s->Create("Radius :", SS_LEFT, CRect(posX, posY + 10 , posX + 70, posY + 32), this);
		s->SetFont(CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT)));
		s->ShowWindow(SW_SHOW);

		ecr->GetClientRect(&r);
		posY += r.bottom;
	}
	
	if (_Emitter->isSpeedBasisEmissionEnabled())
	{
		m_DirectionModeCtrl.SetCurSel((int)AlignOnEmitterDirection);
	}
	else if (!_Emitter->isUserMatrixModeForEmissionDirectionEnabled())
	{
		m_DirectionModeCtrl.SetCurSel((int)Default);
	}
	else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSFXWorldMatrix)
	{
		m_DirectionModeCtrl.SetCurSel((int)LocalToSystem);
	}
	else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSIdentityMatrix)
	{
		m_DirectionModeCtrl.SetCurSel((int)InWorld);
	}
	else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSUserMatrix)
	{
		m_DirectionModeCtrl.SetCurSel((int)LocalToFatherSkeleton);
	}
	else
	{
		nlassert(0);
	}	

	updatePeriodDlg();

	// bypass auto LOD
	nlassert(_Emitter->getOwner() && _Emitter->getOwner()->getOwner());
	NL3D::CParticleSystem &ps = *_Emitter->getOwner()->getOwner();
	CButton *button = (CButton *) GetDlgItem(IDC_BYPASS_AUTOLOD);
	if (ps.isAutoLODEnabled() && !ps.isSharingEnabled())
	{		
		button->EnableWindow(TRUE);
		m_BypassAutoLOD = _Emitter->getBypassAutoLOD();
	}
	else
	{
		button->EnableWindow(FALSE);
	}

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CEmitterDlg::OnConsistentEmission() 
{
	UpdateData();
	_Emitter->enableConsistenEmission(m_ConsistentEmission != 0 ? true : false /* VC6 warning */);
	updateModifiedFlag();
	UpdateData(TRUE);
}

void CEmitterDlg::OnBypassAutoLOD() 
{
	UpdateData();
	_Emitter->setBypassAutoLOD(m_BypassAutoLOD ? true : false);
	UpdateData(TRUE);	
	updateModifiedFlag();
}

void CEmitterDlg::CMaxEmissionCountWrapper::set(const uint32 &count)
{
   if (!E->setMaxEmissionCount((uint8) count))
   {
	   CString mess;
	   mess.LoadString(IDS_PS_NO_FINITE_DURATION);
	   CString errorStr;
	   errorStr.LoadString(IDS_ERROR);
	   ::MessageBox(HWnd, (LPCTSTR) mess, (LPCTSTR) errorStr, MB_ICONEXCLAMATION);	   
	   MaxEmissionCountDlg->updateValueFromReader();
   }
   SSPS->resetAutoCount(Node);
}

void CEmitterDlg::OnSelchangeDirectionMode() 
{
	UpdateData();
	nlassert(_Emitter);
	switch(m_DirectionModeCtrl.GetCurSel())
	{
		case Default:
			_Emitter->enableSpeedBasisEmission(false);
			_Emitter->enableUserMatrixModeForEmissionDirection(false);
		break;
		case AlignOnEmitterDirection:
			_Emitter->enableSpeedBasisEmission(true);
			_Emitter->enableUserMatrixModeForEmissionDirection(false);
		break;
		case InWorld:
			_Emitter->enableSpeedBasisEmission(false);
			_Emitter->enableUserMatrixModeForEmissionDirection(true);
			_Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSIdentityMatrix);
		break;
		case LocalToSystem:
			_Emitter->enableSpeedBasisEmission(false);
			_Emitter->enableUserMatrixModeForEmissionDirection(true);
			_Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSFXWorldMatrix);
		break;
		case LocalToFatherSkeleton:
			_Emitter->enableSpeedBasisEmission(false);
			_Emitter->enableUserMatrixModeForEmissionDirection(true);
			_Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSUserMatrix);
		break;
	}
	updateModifiedFlag();
	UpdateData(FALSE);
}