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

// SceneLightDialog.cpp : implementation file
//

#include "stdafx.h"
#include "W3DView.h"
#include "SceneLightDialog.h"
#include "Utils.H"
#include "MainFrm.H"
#include "W3DViewDoc.H"
#include "Scene.H"
#include "Light.H"


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


//////////////////////////////////////////////////////////////
//
//  CSceneLightDialog
//
CSceneLightDialog::CSceneLightDialog(CWnd* pParent /*=NULL*/)
	: m_CurrentChannel (DIFFUSE),
	  m_InitialStartAtten (0),
	  m_InitialEndAtten (0),
	  m_InitialDistance (0),
	  m_InitialIntensity (0),
	  m_InitialAttenOn (TRUE),
	  CDialog(CSceneLightDialog::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSceneLightDialog)
	//}}AFX_DATA_INIT
    return ;
}


//////////////////////////////////////////////////////////////
//
//  DoDataExchange
//
void
CSceneLightDialog::DoDataExchange (CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSceneLightDialog)
	DDX_Control(pDX, IDC_START_ATTENUATION_SPIN, m_StartAttenSpin);
	DDX_Control(pDX, IDC_END_ATTENUATION_SPIN, m_EndAttenSpin);
	DDX_Control(pDX, IDC_DISTANCE_SPIN, m_DistanceSpin);
	DDX_Control(pDX, IDC_INTENSITY_SLIDER, m_IntensitySlider);
	DDX_Control(pDX, IDC_SLIDER_BLUE, m_blueSlider);
	DDX_Control(pDX, IDC_SLIDER_GREEN, m_greenSlider);
	DDX_Control(pDX, IDC_SLIDER_RED, m_redSlider);
	//}}AFX_DATA_MAP
    return ;
}


BEGIN_MESSAGE_MAP(CSceneLightDialog, CDialog)
	//{{AFX_MSG_MAP(CSceneLightDialog)
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_GRAYSCALE_CHECK, OnGrayscaleCheck)
	ON_BN_CLICKED(IDC_CHANNEL_BOTH_RADIO, OnChannelBothRadio)
	ON_BN_CLICKED(IDC_CHANNEL_DIFFUSE_RADIO, OnChannelDiffuseRadio)
	ON_BN_CLICKED(IDC_CHANNEL_SPECULAR_RADIO, OnChannelSpecularRadio)
	ON_BN_CLICKED(IDC_ATTENUATION_CHECK, OnAttenuationCheck)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


//////////////////////////////////////////////////////////////
//
//  OnInitDialog
//
BOOL
CSceneLightDialog::OnInitDialog (void) 
{
	// Allow the base class to process this message
	CDialog::OnInitDialog ();

	// Center the dialog around the data tree view instead
	// of the direct center of the screen
	::CenterDialogAroundTreeView (m_hWnd);

	// Set the initial ranges for the color sliders
	m_redSlider.SetRange (0, 100);
	m_greenSlider.SetRange (0, 100);
	m_blueSlider.SetRange (0, 100);    

	// Get a pointer to the doc so we can get at the current scene
	// pointer.
	CW3DViewDoc *pCDoc = ::GetCurrentDocument ();
	if (pCDoc && pCDoc->GetScene ()) {
		Vector3 diffuse;
		Vector3 specular;
		pCDoc->GetSceneLight ()->Get_Diffuse (&diffuse);
		pCDoc->GetSceneLight ()->Get_Specular (&specular);

		// Remember these initial settings so we can restore them
		// if the user cancels
		m_InitialRedDiffuse = int(diffuse.X * 100.00F);
		m_InitialGreenDiffuse = int(diffuse.Y * 100.00F);
		m_InitialBlueDiffuse = int(diffuse.Z * 100.00F);        
		m_InitialRedSpecular = int(specular.X * 100.00F);
		m_InitialGreenSpecular = int(specular.Y * 100.00F);
		m_InitialBlueSpecular = int(specular.Z * 100.00F);
		Set_Color_Control_State (diffuse);		

		// Get the light's attenuation
		double start = 0;
		double end = 0;
		pCDoc->GetSceneLight ()->Get_Far_Attenuation_Range (start, end);
		BOOL atten_on = pCDoc->GetSceneLight ()->Get_Flag (LightClass::FAR_ATTENUATION);
		SendDlgItemMessage (IDC_ATTENUATION_CHECK, BM_SETCHECK, (WPARAM)atten_on);

		// Get the light's intensity
		float intensity = pCDoc->GetSceneLight ()->Get_Intensity ();			

		// Attempt to calculate the light's distance from the object
		float distance = 0;
		if (pCDoc->GetDisplayedObject () != NULL) {
			
			// Get the position of the light and the displayed object
			Vector3 light_pos = pCDoc->GetSceneLight ()->Get_Position ();
			Vector3 obj_pos = pCDoc->GetDisplayedObject ()->Get_Position ();

			// Compute the distance of the 2 vectors
			distance = (light_pos - obj_pos).Length ();
		}

		// Set-up the edit controls
		::SetDlgItemFloat (m_hWnd, IDC_START_ATTENUATION_EDIT, start);
		::SetDlgItemFloat (m_hWnd, IDC_END_ATTENUATION_EDIT, end);
		::SetDlgItemFloat (m_hWnd, IDC_DISTANCE_EDIT, distance);
		
		// Set-up the spin controls
		m_DistanceSpin.SetRange (0, 1000000L);
		m_DistanceSpin.SetPos ((distance * 100));
		m_StartAttenSpin.SetRange (0, 1000000L);
		m_StartAttenSpin.SetPos ((start * 100));
		m_EndAttenSpin.SetRange (0, 1000000L);
		m_EndAttenSpin.SetPos ((end * 100));

		// Setup the slider control
		m_IntensitySlider.SetRange (0, 100);
		m_IntensitySlider.SetPos (intensity * 100.0F);

		// Record the initial settings so we can restore them on cancel
		m_InitialStartAtten = start;
		m_InitialEndAtten = end;
		m_InitialDistance = distance;
		m_InitialIntensity = intensity;
		m_InitialAttenOn = atten_on;
	}

	// Check the 'Diffuse' channel by default
	SendDlgItemMessage (IDC_CHANNEL_DIFFUSE_RADIO, BM_SETCHECK, (WPARAM)TRUE);
	Update_Attenuation_Controls ();
	return TRUE;
}


//////////////////////////////////////////////////////////////
//
//  OnHScroll
//
void
CSceneLightDialog::OnHScroll
(
    UINT nSBCode,
    UINT nPos,
    CScrollBar* pScrollBar
)
{
	// Did the intensity slider send this message or did the color sliders?
	if (pScrollBar == GetDlgItem (IDC_INTENSITY_SLIDER)) {
		
		// Update the light's intensity settings
		float intensity = ((float)m_IntensitySlider.GetPos ()) / 100.0F;
		::GetCurrentDocument ()->GetSceneLight ()->Set_Intensity (intensity);

	} else {

		// Are the 3 colors locked together?
		if (SendDlgItemMessage (IDC_GRAYSCALE_CHECK, BM_GETCHECK)) {
			
			// Which color sent this message?
			int iCurrentPos = 0;
			if (pScrollBar == GetDlgItem (IDC_SLIDER_RED)) {

				// Use this color as default
				iCurrentPos = m_redSlider.GetPos ();
			}
			else if (pScrollBar == GetDlgItem (IDC_SLIDER_GREEN)) {

				// Use this color as default
				iCurrentPos = m_greenSlider.GetPos ();
			}
			else {

				// Use this color as default
				iCurrentPos = m_blueSlider.GetPos ();
			}

			// Make all the sliders the same pos
			m_redSlider.SetPos (iCurrentPos);
			m_greenSlider.SetPos (iCurrentPos);
			m_blueSlider.SetPos (iCurrentPos);
		}

		// Update the light to reflect the new settings
		Vector3 color;
		color.X = float(m_redSlider.GetPos ()) / 100.00F;
		color.Y = float(m_greenSlider.GetPos ()) / 100.00F;
		color.Z = float(m_blueSlider.GetPos ()) / 100.00F;
		Update_Light (color);
	}

	// Allow the base class to process this message
	CDialog::OnHScroll (nSBCode, nPos, pScrollBar);
	return ;
}


//////////////////////////////////////////////////////////////
//
//  OnCancel
//
void
CSceneLightDialog::OnCancel (void)
{
	// Get a pointer to the document so we can change the scene light's
	// settings and position
	CW3DViewDoc *pCDoc = ::GetCurrentDocument ();
	if (pCDoc && pCDoc->GetScene ()) {

		Vector3 diffuse;
		diffuse.X = float(m_InitialRedDiffuse) / 100.00F;
		diffuse.Y = float(m_InitialGreenDiffuse) / 100.00F;
		diffuse.Z = float(m_InitialBlueDiffuse) / 100.00F;

		Vector3 specular;
		specular.X = float(m_InitialRedSpecular) / 100.00F;
		specular.Y = float(m_InitialGreenSpecular) / 100.00F;
		specular.Z = float(m_InitialBlueSpecular) / 100.00F;

		// Restore the scene light's color
		pCDoc->GetSceneLight ()->Set_Diffuse (diffuse);
		pCDoc->GetSceneLight ()->Set_Specular (specular);

		// Restore the intensity, attenuation, and distance settings
		pCDoc->GetSceneLight ()->Set_Intensity (m_InitialIntensity);
		pCDoc->GetSceneLight ()->Set_Far_Attenuation_Range (m_InitialStartAtten, m_InitialEndAtten);
		pCDoc->GetSceneLight ()->Set_Flag (LightClass::FAR_ATTENUATION, (m_InitialAttenOn == TRUE));
		Update_Distance (m_InitialDistance);		
	}

	// Allow the base class to process this message
	CDialog::OnCancel ();
	return ;
}


//////////////////////////////////////////////////////////////
//
//  WindowProc
//
LRESULT
CSceneLightDialog::WindowProc
(
    UINT message,
    WPARAM wParam,
    LPARAM lParam
) 
{
	switch (message)
	{
		case WM_NOTIFY:
		{
			// Did this notification come from a spin control?
			NMHDR *pheader = (NMHDR *)lParam;
			if ((pheader != NULL) && (pheader->code == UDN_DELTAPOS)) {
				LPNMUPDOWN pupdown = (LPNMUPDOWN)lParam;
				
				// Get the buddy window associated with this spin control
				HWND hbuddy_wnd = (HWND)SendDlgItemMessage ((int)wParam, UDM_GETBUDDY);
				if (::IsWindow (hbuddy_wnd)) {

					// Get the current value, increment it, and put it back into the control
					float value = ::GetWindowFloat (hbuddy_wnd);
					value += (((float)(pupdown->iDelta)) / 100.0F);
					::SetWindowFloat (hbuddy_wnd, value);

					// Force update the light
					Update_Distance (::GetDlgItemFloat (m_hWnd, IDC_DISTANCE_EDIT));
					Update_Attenuation ();
				}
			}
		}
		break;

		case WM_COMMAND:
		{
			if (HIWORD (wParam) == EN_KILLFOCUS) {
				switch (LOWORD (wParam))
				{
					case IDC_DISTANCE_EDIT:
					{
						Update_Distance (::GetDlgItemFloat (m_hWnd, IDC_DISTANCE_EDIT));
					}
					break;
					
					case IDC_START_ATTENUATION_EDIT:
					case IDC_END_ATTENUATION_EDIT:
					{
						Update_Attenuation ();
					}
					break;
				}
			}
		}
		break;

		case WM_PAINT:
		{
			// Paint the gradients for each color
			::Paint_Gradient (::GetDlgItem (m_hWnd, IDC_RED_GRADIENT), 1, 0, 0);
			::Paint_Gradient (::GetDlgItem (m_hWnd, IDC_GREEN_GRADIENT), 0, 1, 0);
			::Paint_Gradient (::GetDlgItem (m_hWnd, IDC_BLUE_GRADIENT), 0, 0, 1);
		}
		break;
	}
	
	// Allow the base class to process this message
	return CDialog::WindowProc (message, wParam, lParam);
}


//////////////////////////////////////////////////////////////
//
//  OnGrayscaleCheck
//
void
CSceneLightDialog::OnGrayscaleCheck (void)
{
	if (SendDlgItemMessage (IDC_GRAYSCALE_CHECK, BM_GETCHECK)) {
		
		// Make the green and blue sliders the same as red
		m_greenSlider.SetPos (m_redSlider.GetPos ());
		m_blueSlider.SetPos (m_redSlider.GetPos ());

		Vector3 color;
		color.X = float(m_redSlider.GetPos ()) / 100.00F;
		color.Y = float(m_greenSlider.GetPos ()) / 100.00F;
		color.Z = float(m_blueSlider.GetPos ()) / 100.00F;
		Update_Light (color);
	}

	return ;
}


//////////////////////////////////////////////////////////////
//
//  OnChannelBothRadio
//
void
CSceneLightDialog::OnChannelBothRadio (void)
{
	m_CurrentChannel = BOTH;
	return ;
}


//////////////////////////////////////////////////////////////
//
//  OnChannelDiffuseRadio
//
void
CSceneLightDialog::OnChannelDiffuseRadio (void)
{
	// Reset the UI to reflect the current diffuse color
	Vector3 color;
	::GetCurrentDocument ()->GetSceneLight ()->Get_Diffuse (&color);
	Set_Color_Control_State (color);
	m_CurrentChannel = DIFFUSE;
	return ;
}


//////////////////////////////////////////////////////////////
//
//  OnChannelSpecularRadio
//
void
CSceneLightDialog::OnChannelSpecularRadio (void)
{
	// Reset the UI to reflect the current specular color
	Vector3 color;
	::GetCurrentDocument ()->GetSceneLight ()->Get_Specular (&color);
	Set_Color_Control_State (color);
	m_CurrentChannel = SPECULAR;
	return ;
}


//////////////////////////////////////////////////////////////
//
//  Update_Light
//
void
CSceneLightDialog::Update_Light (const Vector3 &color)
{
	// Get a pointer to the document so we can change the scene light's
	// settings
	CW3DViewDoc *pCDoc = ::GetCurrentDocument ();
	if (pCDoc && pCDoc->GetScene ()) {

		// Update the diffuse setting of the scene light if necessary
		if (m_CurrentChannel & DIFFUSE) {
			pCDoc->GetSceneLight ()->Set_Diffuse (color);
		}

		// Update the specular setting of the scene light if necessary
		if (m_CurrentChannel & SPECULAR) {
			pCDoc->GetSceneLight ()->Set_Specular (color);
		}
	}

	return ;
}


//////////////////////////////////////////////////////////////
//
//  Set_Color_Control_State
//
void
CSceneLightDialog::Set_Color_Control_State (const Vector3 &color)
{
	// Should we 'lock' the color sliders together?
	if ((color.X == color.Y) &&
		 (color.X == color.Z)) {
		SendDlgItemMessage (IDC_GRAYSCALE_CHECK, BM_SETCHECK, (WPARAM)TRUE);
	} else {		
		SendDlgItemMessage (IDC_GRAYSCALE_CHECK, BM_SETCHECK, (WPARAM)FALSE);
	}

	// Set the color slider positions
	m_redSlider.SetPos (int(color.X * 100.0F));
	m_greenSlider.SetPos (int(color.Y * 100.0F));
	m_blueSlider.SetPos (int(color.Z * 100.0F));
	return ;
}


//////////////////////////////////////////////////////////////
//
//  Update_Attenuation
//
void
CSceneLightDialog::Update_Attenuation (void)
{
	// Get a pointer to the document so we can change the scene light's
	// settings
	CW3DViewDoc *pCDoc = ::GetCurrentDocument ();
	if (pCDoc && pCDoc->GetScene ()) {

		// Update the attenuation settings
		float start = ::GetDlgItemFloat (m_hWnd, IDC_START_ATTENUATION_EDIT);
		float end = ::GetDlgItemFloat (m_hWnd, IDC_END_ATTENUATION_EDIT);
		pCDoc->GetSceneLight ()->Set_Far_Attenuation_Range (start, end);
	}

	return ;
}


//////////////////////////////////////////////////////////////
//
//  Update_Distance
//
void
CSceneLightDialog::Update_Distance (float distance)
{
	// Get a pointer to the document so we can change the scene light's
	// settings
	CW3DViewDoc *pCDoc = ::GetCurrentDocument ();
	if (pCDoc && pCDoc->GetScene ()) {

		// Get the position of the displayed object
		Vector3 obj_pos (0,0,0);		
		if (pCDoc->GetDisplayedObject ()) {
			obj_pos = pCDoc->GetDisplayedObject ()->Get_Position ();
		}

		// Get the position of the light and the displayed object
		Vector3 light_pos = pCDoc->GetSceneLight ()->Get_Position ();		
		Vector3 new_pos = (light_pos - obj_pos);
		new_pos.Normalize ();
		new_pos = new_pos * distance;

		// Update the attenuation settings
		pCDoc->GetSceneLight ()->Set_Position (new_pos);
	}

	return ;
}


//////////////////////////////////////////////////////////////
//
//  Update_Attenuation_Controls
//
void
CSceneLightDialog::Update_Attenuation_Controls (void)
{
	// Enable or disable the attenuation controls based on the group's checkstate
	BOOL enable = (SendDlgItemMessage (IDC_ATTENUATION_CHECK, BM_GETCHECK) == 1);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_START_ATTENUATION_EDIT), enable);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_START_ATTENUATION_SPIN), enable);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_END_ATTENUATION_EDIT), enable);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_END_ATTENUATION_SPIN), enable);
	return ;
}


//////////////////////////////////////////////////////////////
//
//  OnAttenuationCheck
//
void
CSceneLightDialog::OnAttenuationCheck (void) 
{
	// Update the scene light to reflect the new setting	
	bool enable = (SendDlgItemMessage (IDC_ATTENUATION_CHECK, BM_GETCHECK) == 1);
	CW3DViewDoc *pdoc = ::GetCurrentDocument ();
	pdoc->GetSceneLight ()->Set_Flag (LightClass::FAR_ATTENUATION, enable);

	// Update the dialog controls to reflect the new setting
	Update_Attenuation_Controls ();
	return ;
}
