/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 

This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  

Doom 3 Source Code 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.

Doom 3 Source Code 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 Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/




#include "qe3.h"
#include "Radiant.h"
#include "../../game/game.h"
#include "../comafx/DialogColorPicker.h"
#include "LightDlg.h"

#ifdef ID_DEBUG_MEMORY
#undef new
#undef DEBUG_NEW
#define DEBUG_NEW new
#endif


void CLightInfo::Defaults() {
	pointLight = true;
	fallOff = 1;
	strTexture = "";
	equalRadius = true;
	explicitStartEnd = false;
	lightRadius.Zero();
	lightTarget.Zero();
	lightRight.Zero();
	lightUp.Zero();
	lightStart.Zero();
	lightEnd.Zero();
	lightCenter.Zero();
	hasCenter = false;
	isParallel = false;
	castShadows = true;
	castSpecular = true;
	castDiffuse = true;
	rotate = false;
	strobe = false;
	rotateSpeed = 0;
	strobeSpeed = 0;
	color[0] = color[1] = color[2] = 255;
	fogDensity[0] = fogDensity[1] = fogDensity[2] = 0;
	fog = false;
	lightRadius[0] = lightRadius[1] = lightRadius[2] = 300;
}


void CLightInfo::DefaultPoint() {
	idVec3 oldColor = color;
	Defaults();
	color = oldColor;
	pointLight = true;
}

void CLightInfo::DefaultProjected() {
	idVec3 oldColor = color;
	Defaults();
	color = oldColor;
	pointLight = false;
	lightTarget[2] = -256;
	lightUp[1] = -128;
	lightRight[0] = -128;
}

void CLightInfo::FromDict( const idDict *e ) {

	lightRadius.Zero();
	lightTarget.Zero();
	lightRight.Zero();
	lightUp.Zero();
	lightStart.Zero();
	lightEnd.Zero();
	lightCenter.Zero();

	castShadows = !e->GetBool("noshadows");
	castSpecular = !e->GetBool("nospecular");
	castDiffuse = !e->GetBool("nodiffuse");
	fallOff = e->GetFloat("falloff");
	strTexture = e->GetString("texture");

	isParallel = e->GetBool("parallel");

	if (!e->GetVector("_color", "", color)) {
		color[0] = color[1] = color[2] = 1;
	}
	// windows needs 0-255 scale
	color[0] *= 255;
	color[1] *= 255;
	color[2] *= 255;

	if (e->GetVec4("fog", "", fogDensity)) {
		fog = true;
	} else {
		fog = false;
	}

	if (e->GetVector("light_right","", lightRight)) {
		// projected light
		pointLight = false;
		e->GetVector("light_target", "", lightTarget);
		e->GetVector("light_up", "", lightUp);
		if (e->GetVector("light_start", "", lightStart)) {
			// explicit start and end points
			explicitStartEnd = true;
			if (!e->GetVector("light_end", "", lightEnd)) {
				// no end, use target
				VectorCopy(lightTarget, lightEnd);
			}
		} else {
			explicitStartEnd = false;
			// create a start a quarter of the way to the target
			lightStart = lightTarget * 0.25;
			VectorCopy(lightTarget, lightEnd);
		}
	} else {
		pointLight = true;
		if (e->GetVector("light_radius", "", lightRadius)) {
			equalRadius = false;
		} else {
			float radius = e->GetFloat("light");
			if (radius == 0) {
				radius = 300;
			}
			lightRadius[0] = lightRadius[1] = lightRadius[2] = radius;
			equalRadius = true;
		}
		if (e->GetVector("light_center", "", lightCenter)) {
			hasCenter = true;
		}
	}
}

void CLightInfo::ToDictFromDifferences ( idDict *e, const idDict *differences ) {
    for ( int i = 0 ; i < differences->GetNumKeyVals () ; i ++ ) {
        const idKeyValue *kv = differences->GetKeyVal( i );
        
        if ( kv->GetValue().Length() > 0 ) {
            e->Set ( kv->GetKey() ,kv->GetValue() );
		} else {
	        e->Delete ( kv->GetKey() );
		}

        common->Printf( "Applied difference: %s %s\n" , kv->GetKey().c_str() , kv->GetValue().c_str() );
    }
}

//write all info to a dict, regardless of light type
void CLightInfo::ToDictWriteAllInfo( idDict *e ) {
    e->Set("noshadows", (!castShadows) ? "1" : "0");
	e->Set("nospecular", (!castSpecular) ? "1" : "0");
	e->Set("nodiffuse", (!castDiffuse) ? "1" : "0");

	e->SetFloat("falloff",fallOff);
	
	if (strTexture.GetLength() > 0 ) {
		e->Set("texture", strTexture);
	}

	idVec3 temp = color;
	temp /= 255;
	e->SetVector("_color", temp);

	if (!equalRadius) {
		e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[1], lightRadius[2]));
	} else {
		e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[0], lightRadius[0]));
	}

	e->Set("light_center", va("%g %g %g", lightCenter[0], lightCenter[1], lightCenter[2]));
    e->Set("parallel", isParallel?"1":"0");

    e->Set("light_target", va("%g %g %g", lightTarget[0], lightTarget[1], lightTarget[2]));
	e->Set("light_up", va("%g %g %g", lightUp[0], lightUp[1], lightUp[2]));
	e->Set("light_right", va("%g %g %g", lightRight[0], lightRight[1], lightRight[2]));
    e->Set("light_start", va("%g %g %g", lightStart[0], lightStart[1], lightStart[2]));
	e->Set("light_end", va("%g %g %g", lightEnd[0], lightEnd[1], lightEnd[2]));
}

void CLightInfo::ToDict( idDict *e ) {

	e->Delete("noshadows");
	e->Delete("nospecular");
	e->Delete("nodiffuse");
	e->Delete("falloff");
	e->Delete("parallel");
	e->Delete("texture");
	e->Delete("_color");
	e->Delete("fog");
	e->Delete("light_target");
	e->Delete("light_right");
	e->Delete("light_up");
	e->Delete("light_start");
	e->Delete("light_end");
	e->Delete("light_radius");
	e->Delete("light_center");
	e->Delete("light");

	e->Set("noshadows", (!castShadows) ? "1" : "0");
	e->Set("nospecular", (!castSpecular) ? "1" : "0");
	e->Set("nodiffuse", (!castDiffuse) ? "1" : "0");

	e->SetFloat("falloff",fallOff);
	
	if (strTexture.GetLength() > 0) {
		e->Set("texture", strTexture);
	}

	idVec3 temp = color;
	temp /= 255;
	e->SetVector("_color", temp);

	if (fog) {
		e->Set("fog", va("%g %g %g %g", fogDensity[0]/255.0, fogDensity[1]/255.0, fogDensity[2]/255.0, fogDensity[3]/255.0));
	}

	if (pointLight) {
		if (!equalRadius) {
			e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[1], lightRadius[2]));
		} else {
			e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[0], lightRadius[0]));
		}

		if (hasCenter) {
			e->Set("light_center", va("%g %g %g", lightCenter[0], lightCenter[1], lightCenter[2]));
		}

		if (isParallel) {
			e->Set("parallel", "1");
		}
	} else {
		e->Set("light_target", va("%g %g %g", lightTarget[0], lightTarget[1], lightTarget[2]));
		e->Set("light_up", va("%g %g %g", lightUp[0], lightUp[1], lightUp[2]));
		e->Set("light_right", va("%g %g %g", lightRight[0], lightRight[1], lightRight[2]));
		if (explicitStartEnd) {
			e->Set("light_start", va("%g %g %g", lightStart[0], lightStart[1], lightStart[2]));
			e->Set("light_end", va("%g %g %g", lightEnd[0], lightEnd[1], lightEnd[2]));
		}
	}
}

CLightInfo::CLightInfo() {
	Defaults();
}



/////////////////////////////////////////////////////////////////////////////
// CLightDlg dialog

CLightDlg *g_LightDialog = NULL;


CLightDlg::CLightDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CLightDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CLightDlg)
	m_bEqualRadius = FALSE;
	m_bExplicitFalloff = FALSE;
	m_bPointLight = FALSE;
	m_bCheckProjected = FALSE;
	m_fFallloff = 0.0f;
	m_nFalloff = -1;
	m_bRotate = FALSE;
	m_bShadows = FALSE;
	m_bSpecular = FALSE;
	m_bDiffuse = FALSE;
	m_fEndX = 0.0f;
	m_fEndY = 0.0f;
	m_fEndZ = 0.0f;
	m_fRadiusX = 0.0f;
	m_fRadiusY = 0.0f;
	m_fRadiusZ = 0.0f;
	m_fRightX = 0.0f;
	m_fRightY = 0.0f;
	m_fRightZ = 0.0f;
	m_fRotate = 0.0f;
	m_fStartX = 0.0f;
	m_fStartY = 0.0f;
	m_fStartZ = 0.0f;
	m_fTargetX = 0.0f;
	m_fTargetY = 0.0f;
	m_fTargetZ = 0.0f;
	m_fUpX = 0.0f;
	m_fUpY = 0.0f;
	m_fUpZ = 0.0f;
	m_hasCenter = FALSE;
	m_centerX = 0.0f;
	m_centerY = 0.0f;
	m_centerZ = 0.0f;
    m_bIsParallel = FALSE;
	//}}AFX_DATA_INIT
	m_drawMaterial = new idGLDrawableMaterial();
}

CLightDlg::~CLightDlg() {
	delete m_drawMaterial;
}

void CLightDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CLightDlg)
	if ( com_editorActive ) {
		DDX_Control(pDX, IDC_LIGHTPREVIEW, m_wndPreview);
	}
	DDX_Control(pDX, IDC_COMBO_TEXTURE, m_wndLights);
	DDX_Check(pDX, IDC_CHECK_EQUALRADIUS, m_bEqualRadius);
	DDX_Check(pDX, IDC_CHECK_EXPLICITFALLOFF, m_bExplicitFalloff);
	DDX_Check(pDX, IDC_CHECK_POINT, m_bPointLight);
	DDX_Check(pDX, IDC_CHECK_PROJECTED, m_bCheckProjected);
	DDX_Radio(pDX, IDC_RADIO_FALLOFF, m_nFalloff);
	DDX_Check(pDX, IDC_CHECK_SHADOWS, m_bShadows);
	DDX_Check(pDX, IDC_CHECK_SPECULAR, m_bSpecular);
	DDX_Check(pDX, IDC_CHECK_DIFFUSE, m_bDiffuse);
    DDX_Check(pDX , IDC_CHECK_PARALLEL , m_bIsParallel );
	DDX_Text(pDX, IDC_EDIT_ENDX, m_fEndX);
	DDX_Text(pDX, IDC_EDIT_ENDY, m_fEndY);
	DDX_Text(pDX, IDC_EDIT_ENDZ, m_fEndZ);
	DDX_Text(pDX, IDC_EDIT_RADIUSX, m_fRadiusX);
	DDX_Text(pDX, IDC_EDIT_RADIUSY, m_fRadiusY);
	DDX_Text(pDX, IDC_EDIT_RADIUSZ, m_fRadiusZ);
	DDX_Text(pDX, IDC_EDIT_RIGHTX, m_fRightX);
	DDX_Text(pDX, IDC_EDIT_RIGHTY, m_fRightY);
	DDX_Text(pDX, IDC_EDIT_RIGHTZ, m_fRightZ);
	DDX_Text(pDX, IDC_EDIT_STARTX, m_fStartX);
	DDX_Text(pDX, IDC_EDIT_STARTY, m_fStartY);
	DDX_Text(pDX, IDC_EDIT_STARTZ, m_fStartZ);
	DDX_Text(pDX, IDC_EDIT_TARGETX, m_fTargetX);
	DDX_Text(pDX, IDC_EDIT_TARGETY, m_fTargetY);
	DDX_Text(pDX, IDC_EDIT_TARGETZ, m_fTargetZ);
	DDX_Text(pDX, IDC_EDIT_UPX, m_fUpX);
	DDX_Text(pDX, IDC_EDIT_UPY, m_fUpY);
	DDX_Text(pDX, IDC_EDIT_UPZ, m_fUpZ);
	DDX_Check(pDX, IDC_CHECK_CENTER, m_hasCenter);
	DDX_Text(pDX, IDC_EDIT_CENTERX, m_centerX);
	DDX_Text(pDX, IDC_EDIT_CENTERY, m_centerY);
	DDX_Text(pDX, IDC_EDIT_CENTERZ, m_centerZ);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CLightDlg, CDialog)
	//{{AFX_MSG_MAP(CLightDlg)
	ON_BN_CLICKED(IDC_BTN_TEXTURE, OnBtnTexture)
	ON_BN_CLICKED(IDC_CHECK_EQUALRADIUS, OnCheckEqualradius)
	ON_BN_CLICKED(IDC_CHECK_EXPLICITFALLOFF, OnCheckExplicitfalloff)
	ON_BN_CLICKED(IDC_CHECK_POINT, OnCheckPoint)
	ON_BN_CLICKED(IDC_CHECK_PROJECTED, OnCheckProjected)
	ON_BN_CLICKED(IDC_RADIO_FALLOFF, OnRadioFalloff)
	ON_BN_CLICKED(IDC_APPLY, OnApply)
    ON_BN_CLICKED(IDC_APPLY_DIFFERENT, OnApplyDifferences)
	ON_BN_CLICKED(IDC_BTN_COLOR, OnBtnColor)
	ON_WM_CTLCOLOR()
	ON_CBN_SELCHANGE(IDC_COMBO_TEXTURE, OnSelchangeComboTexture)
	ON_BN_CLICKED(IDC_CHECK_CENTER, OnCheckCenter)
	ON_BN_CLICKED(IDC_CHECK_PARALLEL, OnCheckParallel)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLightDlg message handlers

void CLightDlg::SetSpecifics() {
	if (lightInfo.pointLight) {
		GetDlgItem(IDC_EDIT_RADIUSY)->EnableWindow(!lightInfo.equalRadius);
		GetDlgItem(IDC_EDIT_RADIUSZ)->EnableWindow(!lightInfo.equalRadius);
		GetDlgItem(IDC_EDIT_CENTERX)->EnableWindow(lightInfo.hasCenter);
		GetDlgItem(IDC_EDIT_CENTERY)->EnableWindow(lightInfo.hasCenter);
		GetDlgItem(IDC_EDIT_CENTERZ)->EnableWindow(lightInfo.hasCenter);
	} else {
		GetDlgItem(IDC_EDIT_STARTX)->EnableWindow(lightInfo.explicitStartEnd);
		GetDlgItem(IDC_EDIT_STARTY)->EnableWindow(lightInfo.explicitStartEnd);
		GetDlgItem(IDC_EDIT_STARTZ)->EnableWindow(lightInfo.explicitStartEnd);
		GetDlgItem(IDC_EDIT_ENDX)->EnableWindow(lightInfo.explicitStartEnd);
		GetDlgItem(IDC_EDIT_ENDY)->EnableWindow(lightInfo.explicitStartEnd);
		GetDlgItem(IDC_EDIT_ENDZ)->EnableWindow(lightInfo.explicitStartEnd);
	}
}

void CLightDlg::EnableControls() {
	GetDlgItem(IDC_CHECK_EQUALRADIUS)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RADIUSX)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RADIUSY)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RADIUSZ)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_RADIO_FALLOFF)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_RADIO_FALLOFF2)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_RADIO_FALLOFF3)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_TARGETX)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_TARGETY)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_TARGETZ)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RIGHTX)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RIGHTY)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_RIGHTZ)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_UPX)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_UPY)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_UPZ)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_STARTX)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_STARTY)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_STARTZ)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_ENDX)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_ENDY)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_ENDZ)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_CHECK_EXPLICITFALLOFF)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_CHECK_POINT)->EnableWindow(!lightInfo.pointLight);
	GetDlgItem(IDC_CHECK_PROJECTED)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_CENTERX)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_CENTERY)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_EDIT_CENTERZ)->EnableWindow(lightInfo.pointLight);
	GetDlgItem(IDC_CHECK_CENTER)->EnableWindow(lightInfo.pointLight);

	reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_PROJECTED))->SetCheck(!lightInfo.pointLight);
	reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_POINT))->SetCheck(lightInfo.pointLight);

	SetSpecifics();
}

void CLightDlg::UpdateDialogFromLightInfo( void ) {
	m_hasCenter = lightInfo.hasCenter;
	m_bEqualRadius = lightInfo.equalRadius;
	m_bExplicitFalloff = lightInfo.explicitStartEnd;
	m_bPointLight = lightInfo.pointLight;
	m_bCheckProjected = !lightInfo.pointLight;
	m_fFallloff = lightInfo.fallOff;
	if (lightInfo.fallOff < 0.35) {
		m_nFalloff = 0;
	} else if (lightInfo.fallOff < 0.70) {
		m_nFalloff = 1;
	} else {
		m_nFalloff = 2;
	}
	//m_bFog = lightInfo.fog;
	m_bRotate = lightInfo.rotate;
	m_bShadows = lightInfo.castShadows;
	m_bSpecular = lightInfo.castSpecular;
	//m_bStrobe = lightInfo.strobe;
	//m_fStrobe = lightInfo.strobeSpeed;
	int sel = m_wndLights.FindStringExact(-1, lightInfo.strTexture);
	m_wndLights.SetCurSel(sel);
	if (sel >= 0) {
		m_drawMaterial->setMedia(lightInfo.strTexture);
	} else {
		m_drawMaterial->setMedia(lightInfo.strTexture);
	}

	m_bDiffuse = lightInfo.castDiffuse;
	m_fEndX = lightInfo.lightEnd[0];
	m_fEndY = lightInfo.lightEnd[1];
	m_fEndZ = lightInfo.lightEnd[2];
	m_fRadiusX = lightInfo.lightRadius[0];
	m_fRadiusY = lightInfo.lightRadius[1];
	m_fRadiusZ = lightInfo.lightRadius[2];
	m_fRightX = lightInfo.lightRight[0];
	m_fRightY = lightInfo.lightRight[1];
	m_fRightZ = lightInfo.lightRight[2];
	//m_bRotate = lightInfo.rotate;
	//m_fRotate = lightInfo.rotateSpeed;
	m_fStartX = lightInfo.lightStart[0];
	m_fStartY = lightInfo.lightStart[1];
	m_fStartZ = lightInfo.lightStart[2];
	m_fTargetX = lightInfo.lightTarget[0];
	m_fTargetY = lightInfo.lightTarget[1];
	m_fTargetZ = lightInfo.lightTarget[2];
	m_fUpX = lightInfo.lightUp[0];
	m_fUpY = lightInfo.lightUp[1];
	m_fUpZ = lightInfo.lightUp[2];
	VectorCopy(lightInfo.color, color);
	//m_fFogAlpha = lightInfo.fogDensity[3];
	m_centerX = lightInfo.lightCenter[0];
	m_centerY = lightInfo.lightCenter[1];
	m_centerZ = lightInfo.lightCenter[2];

    //jhefty - added parallel light updating
    m_bIsParallel = lightInfo.isParallel;

	UpdateData(FALSE);
}

void CLightDlg::UpdateLightInfoFromDialog( void ) {
	UpdateData( TRUE );
	
	lightInfo.pointLight = ( m_bPointLight != FALSE );
	lightInfo.equalRadius = ( m_bEqualRadius != FALSE );
	lightInfo.explicitStartEnd = ( m_bExplicitFalloff != FALSE );
	
	if (lightInfo.pointLight) {
		if (m_nFalloff == 0) {
			m_fFallloff = 0.0;
		} else if (m_nFalloff == 1) {
			m_fFallloff = 0.5;
		} else {
			m_fFallloff = 1.0;
		}
	}

	lightInfo.fallOff = m_fFallloff;

	//lightInfo.fog = m_bFog;
	lightInfo.rotate = ( m_bRotate != FALSE );
	lightInfo.castShadows = ( m_bShadows != FALSE );
	lightInfo.castSpecular = ( m_bSpecular != FALSE );

	VectorCopy(color, lightInfo.color);
    lightInfo.isParallel = (m_bIsParallel == TRUE);

	//lightInfo.fogDensity[3] = m_fFogAlpha;

	//lightInfo.strobe = m_bStrobe;
	//lightInfo.strobeSpeed = m_fStrobe;
	//lightInfo.rotate = m_bRotate; 
	//lightInfo.rotateSpeed = m_fRotate;

	int sel = m_wndLights.GetCurSel();
	CString str("");
	if (sel >= 0) {
		m_wndLights.GetLBText(sel, str);
	}
	lightInfo.strTexture = str;

	lightInfo.castDiffuse = ( m_bDiffuse != FALSE );
	lightInfo.lightEnd[0] = m_fEndX;
	lightInfo.lightEnd[1] = m_fEndY;
	lightInfo.lightEnd[2] = m_fEndZ;
	lightInfo.lightRadius[0] = m_fRadiusX;
	lightInfo.lightRadius[1] = m_fRadiusY; 
	lightInfo.lightRadius[2] = m_fRadiusZ; 
	lightInfo.lightRight[0] = m_fRightX;
	lightInfo.lightRight[1] = m_fRightY;
	lightInfo.lightRight[2] = m_fRightZ;
	lightInfo.lightStart[0] = m_fStartX;
	lightInfo.lightStart[1] = m_fStartY;
	lightInfo.lightStart[2] = m_fStartZ;
	lightInfo.lightTarget[0] = m_fTargetX;
	lightInfo.lightTarget[1] = m_fTargetY; 
	lightInfo.lightTarget[2] = m_fTargetZ;
	lightInfo.lightUp[0] = m_fUpX;
	lightInfo.lightUp[1] = m_fUpY; 
	lightInfo.lightUp[2] = m_fUpZ;

	lightInfo.hasCenter = ( m_hasCenter != FALSE );
	lightInfo.lightCenter[0] = m_centerX;
	lightInfo.lightCenter[1] = m_centerY;
	lightInfo.lightCenter[2] = m_centerZ;
}

void CLightDlg::SaveLightInfo( const idDict *differences ) {

	if ( com_editorActive ) {

		// used from Radiant
		for ( brush_t *b = selected_brushes.next; b && b != &selected_brushes; b = b->next ) {
			if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) {
				if ( differences ) {
					lightInfo.ToDictFromDifferences( &b->owner->epairs, differences );
				} else {
					lightInfo.ToDict( &b->owner->epairs );
				}
				Brush_Build( b );
			}
		}

	} else {

		// used in-game
		idList<idEntity *> list;

		list.SetNum( 128 );
		int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
		list.SetNum( count );

		for ( int i = 0; i < count; i++ ) {
			if ( differences ) {
				gameEdit->EntityChangeSpawnArgs( list[i], differences );
				gameEdit->EntityUpdateChangeableSpawnArgs( list[i], NULL );
			} else {
				idDict newArgs;
				lightInfo.ToDict( &newArgs );
				gameEdit->EntityChangeSpawnArgs( list[i], &newArgs );
				gameEdit->EntityUpdateChangeableSpawnArgs( list[i], NULL );
			}
			gameEdit->EntityUpdateVisuals( list[i] );
		}
    }
}

void CLightDlg::ColorButtons() {
	CRect r;

	CClientDC dc(this);
	
	CButton *pBtn = (CButton *)GetDlgItem(IDC_BTN_COLOR);
	pBtn->GetClientRect(&r);
	colorBitmap.DeleteObject();
	colorBitmap.CreateCompatibleBitmap(&dc, r.Width(), r.Height());
	CDC MemDC;
	MemDC.CreateCompatibleDC(&dc);
	CBitmap *pOldBmp = MemDC.SelectObject(&colorBitmap);
	{
		CBrush br(RGB(color[0], color[1], color[2])); 
		MemDC.FillRect(r,&br);
	}
	dc.SelectObject(pOldBmp);
	pBtn->SetBitmap(HBITMAP(colorBitmap)); 
}


void CLightDlg::LoadLightTextures() {
	int count = declManager->GetNumDecls( DECL_MATERIAL );
	int i;
	const idMaterial *mat;
	for (i = 0; i < count; i++) {
		mat = declManager->MaterialByIndex(i, false);
		idStr str = mat->GetName();
		str.ToLower();
		if (str.Icmpn("lights/", strlen("lights/")) == 0 || str.Icmpn("fogs/", strlen("fogs/")) == 0) {
			m_wndLights.AddString(mat->GetName());
		}
	}
}

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

	com_editors |= EDITOR_LIGHT;

	UpdateDialog( true );

	LoadLightTextures();

	if ( com_editorActive ) {
		m_wndPreview.setDrawable(m_drawMaterial);
	}

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

void CLightDlg::OnDestroy() {

	com_editors &= ~EDITOR_LIGHT;

	return CDialog::OnDestroy();
}

void CLightDlg::OnBtnTexture() 
{
	// TODO: Add your control notification handler code here
	
}

void CLightDlg::OnCheckEqualradius() 
{
	lightInfo.equalRadius = ( reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_EQUALRADIUS))->GetCheck() != 0 );
	SetSpecifics();
}

void CLightDlg::OnCheckExplicitfalloff() 
{
	lightInfo.explicitStartEnd = ( reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_EXPLICITFALLOFF))->GetCheck() != 0 );
	SetSpecifics();
}

void CLightDlg::OnCheckPoint() 
{
	lightInfo.DefaultPoint();
	UpdateDialogFromLightInfo();
	EnableControls();
}

void CLightDlg::OnCheckProjected() 
{
	lightInfo.DefaultProjected();
	UpdateDialogFromLightInfo();
	EnableControls();
}

void CLightDlg::OnRadioFalloff() 
{
}

void CLightDlg::OnOK() {
	UpdateLightInfoFromDialog();
	SaveLightInfo( NULL );
	Sys_UpdateWindows(W_ALL);
	CDialog::OnOK();
}

entity_t *SingleLightSelected() {
	if ( QE_SingleBrush( true, true ) ) {
		brush_t *b = selected_brushes.next;
		if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) {
			return b->owner;
		}
	}
	return NULL;
}

void CLightDlg::UpdateDialog( bool updateChecks )
{	
	CString title;

	lightInfo.Defaults();
	lightInfoOriginal.Defaults ();

	if ( com_editorActive ) {
		// used from Radiant
		entity_t *e = SingleLightSelected();
		if ( e ) {
			lightInfo.FromDict(&e->epairs);
			lightInfoOriginal.FromDict(&e->epairs); //our original copy of the values that we compare against for apply differences
			title = "Light Editor";
		} else {   
			//find the last brush belonging to the last entity selected and use that as the source
			e = NULL;
			for ( brush_t *b = selected_brushes.next ; b != &selected_brushes ; b = b->next ) {
				if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) {
					e = b->owner;
					break;
				}
			}

			if ( e ) {
				lightInfo.FromDict( &e->epairs );
				lightInfoOriginal.FromDict(&e->epairs); //our original copy of the values that we compaer against for apply differences
				title = "Light Editor - (Multiple lights selected)";
			} else {
				title = "Light Editor - (No lights selected)";
			}
		}
	} else {
		// used in-game
		idList<idEntity *> list;

		list.SetNum( 128 );
		int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
		list.SetNum( count );

		if ( count > 0 ) {
			lightInfo.FromDict( gameEdit->EntityGetSpawnArgs( list[count-1] ) );
			title = "Light Editor";
		} else {
			title = "Light Editor - (No entities selected)";
		}
	}

	SetWindowText( title );

	UpdateDialogFromLightInfo();
	ColorButtons();

	if ( updateChecks ) {
		EnableControls();
	}
}

void LightEditorInit( const idDict *spawnArgs ) {
	if ( renderSystem->IsFullScreen() ) {
		common->Printf( "Cannot run the light editor in fullscreen mode.\n"
					"Set r_fullscreen to 0 and vid_restart.\n" );
		return;
	}

	if ( g_LightDialog == NULL ) {
		InitAfx();
		g_LightDialog = new CLightDlg();
	}

	if ( g_LightDialog->GetSafeHwnd() == NULL ) {
		g_LightDialog->Create( IDD_DIALOG_LIGHT );
		CRect rct;
		LONG lSize = sizeof( rct );
		if ( LoadRegistryInfo( "Radiant::LightWindow", &rct, &lSize ) ) {
			g_LightDialog->SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE);
		}
	}

	idKeyInput::ClearStates();

	g_LightDialog->ShowWindow( SW_SHOW );
	g_LightDialog->SetFocus();
	g_LightDialog->UpdateDialog( true );

	if ( spawnArgs ) {
		// FIXME: select light based on spawn args
	}
}

void LightEditorRun( void ) {
#if _MSC_VER >= 1300
	MSG *msg = AfxGetCurrentMessage();			// TODO Robert fix me!!
#else
	MSG *msg = &m_msgCur;
#endif

	while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) {
		// pump message
		if ( !AfxGetApp()->PumpMessage() ) {
		}
	}
}

void LightEditorShutdown( void ) {
	delete g_LightDialog;
	g_LightDialog = NULL;
}

void UpdateLightInspector() {
	if ( g_LightDialog && g_LightDialog->GetSafeHwnd() != NULL ) {
		g_LightDialog->UpdateDialog(true);   //jhefty - update ALL info about the light, including check boxes
	}
}

void CLightDlg::OnApply() {
	UpdateLightInfoFromDialog();
	SaveLightInfo( NULL );
	Sys_UpdateWindows( W_ALL );
}

void UpdateLightDialog( float r, float g, float b, float a ) {
	UpdateRadiantColor( 0.0f, 0.0f, 0.0f, 0.0f );
	g_LightDialog->UpdateColor( r, g, b, a );
}

void CLightDlg::UpdateColor( float r, float g, float b, float a ) {
	color[0] = a * r;
	color[1] = a * g;
	color[2] = a * b;
	ColorButtons();
	UpdateLightInfoFromDialog();
	SaveLightInfo( NULL );
	Sys_UpdateWindows( W_CAMERA );
}

void CLightDlg::OnBtnColor() {
	int r, g, b;
	float ob;
	r = color[0];
	g = color[1];
	b = color[2];
	if ( DoNewColor( &r, &g, &b, &ob, UpdateLightDialog ) ) {
		color[0] = ob * r;
		color[1] = ob * g;
		color[2] = ob * b;
		ColorButtons();
	}
}

void CLightDlg::OnCancel() {
	CDialog::OnCancel();
}

HBRUSH CLightDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
	HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

	return hbr;
}

BOOL CLightDlg::DestroyWindow() 
{
	if (GetSafeHwnd())
	{
		CRect rct;
		GetWindowRect(rct);
		SaveRegistryInfo("Radiant::LightWindow", &rct, sizeof(rct));
	}
	return CDialog::DestroyWindow();
}

void CLightDlg::OnSelchangeComboTexture() 
{
	UpdateData(TRUE);
	int sel = m_wndLights.GetCurSel();
	CString str;
	if (sel >= 0) {
		m_wndLights.GetLBText(sel, str);
		m_drawMaterial->setMedia(str);
		if ( com_editorActive ) {
			m_wndPreview.RedrawWindow();
		}
	}
	Sys_UpdateWindows(W_ALL);
}

void CLightDlg::OnCheckCenter() 
{
	if (reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_CENTER))->GetCheck()) {
		lightInfo.hasCenter = true;
		lightInfo.lightCenter.x = 0;
		lightInfo.lightCenter.y = 0;
		lightInfo.lightCenter.z = 32;
	} else {
		lightInfo.hasCenter = false;
		lightInfo.lightCenter.Zero();
	}
	UpdateDialogFromLightInfo();
	SetSpecifics();
}

void CLightDlg::OnCheckParallel() {
	if ( reinterpret_cast<CButton*>(GetDlgItem(IDC_CHECK_PARALLEL))->GetCheck() ) {
		lightInfo.hasCenter = true;
		lightInfo.isParallel = true;
		lightInfo.lightCenter.x = 0;
		lightInfo.lightCenter.y = 0;
		lightInfo.lightCenter.z = 32;
	} else {
		lightInfo.isParallel = false;
		lightInfo.hasCenter = false;
	}

	UpdateDialogFromLightInfo();
	SetSpecifics();
}

//jhefty - only apply settings that are different
void CLightDlg::OnApplyDifferences () {
	idDict differences, modified, original;

	UpdateLightInfoFromDialog();

	lightInfo.ToDict( &modified );
	lightInfoOriginal.ToDictWriteAllInfo( &original );

	differences = modified;

	// jhefty - compile a set of modified values to apply
	for ( int i = 0; i < modified.GetNumKeyVals (); i ++ ) {
		const idKeyValue* valModified = modified.GetKeyVal ( i );
		const idKeyValue* valOriginal = original.FindKey ( valModified->GetKey() );

		//if it hasn't changed, remove it from the list of values to apply
		if ( !valOriginal || ( valModified->GetValue() == valOriginal->GetValue() ) ) {
			differences.Delete ( valModified->GetKey() );
		}
	}

	SaveLightInfo( &differences );

	lightInfoOriginal.FromDict( &modified );

	Sys_UpdateWindows( W_ALL );
}
