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

// CameraSettingsDialog.cpp : implementation file
//

#include "stdafx.h"
#include "w3dview.h"
#include "w3dviewdoc.h"
#include "graphicview.h"
#include "CameraSettingsDialog.h"
#include "utils.h"
#include "camera.h"
#include "viewerscene.h"


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


/////////////////////////////////////////////////////////////////////////////
//
// CameraSettingsDialogClass
//
/////////////////////////////////////////////////////////////////////////////
CameraSettingsDialogClass::CameraSettingsDialogClass(CWnd* pParent /*=NULL*/)
	: CDialog(CameraSettingsDialogClass::IDD, pParent)
{
	//{{AFX_DATA_INIT(CameraSettingsDialogClass)
	//}}AFX_DATA_INIT
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// DoDataExchange
//
/////////////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::DoDataExchange (CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CameraSettingsDialogClass)
	DDX_Control(pDX, IDC_LENS_SPIN, m_LensSpin);
	DDX_Control(pDX, IDC_FAR_CLIP_SPIN, m_FarClipSpin);
	DDX_Control(pDX, IDC_VFOV_SPIN, m_VFOVSpin);
	DDX_Control(pDX, IDC_NEAR_CLIP_SPIN, m_NearClipSpin);
	DDX_Control(pDX, IDC_HFOV_SPIN, m_HFOVSpin);
	//}}AFX_DATA_MAP
	return ;
}


BEGIN_MESSAGE_MAP(CameraSettingsDialogClass, CDialog)
	//{{AFX_MSG_MAP(CameraSettingsDialogClass)
	ON_BN_CLICKED(IDC_FOV_CHECK, OnFovCheck)
	ON_BN_CLICKED(IDC_CLIP_PLANE_CHECK, OnClipPlaneCheck)
	ON_BN_CLICKED(IDC_RESET, OnReset)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



/////////////////////////////////////////////////////////////////////////////
//
// OnInitDialog
//
/////////////////////////////////////////////////////////////////////////////
BOOL
CameraSettingsDialogClass::OnInitDialog (void)
{
	CDialog::OnInitDialog ();
	
	CW3DViewDoc *doc				= ::GetCurrentDocument ();
	CGraphicView *graphic_view = doc->GetGraphicView ();
	CameraClass *camera			= graphic_view->GetCamera ();

	//
	//	Enable/disable the group boxes
	//
	SendDlgItemMessage (IDC_FOV_CHECK, BM_SETCHECK, (WPARAM)doc->Is_FOV_Manual ());
	SendDlgItemMessage (IDC_CLIP_PLANE_CHECK, BM_SETCHECK, (WPARAM)doc->Are_Clip_Planes_Manual ());

	float znear = 0;
	float zfar = 0;
	camera->Get_Clip_Planes (znear, zfar);		
	::Initialize_Spinner (m_NearClipSpin, znear, 0.0F, 999999.0F);
	::Initialize_Spinner (m_FarClipSpin, zfar, 1.0F, 999999.0F);	
	
	//
	//	Setup the FOV controls
	//
	int hfov_deg = (int)RAD_TO_DEG (camera->Get_Horizontal_FOV ());
	int vfov_deg = (int)RAD_TO_DEG (camera->Get_Vertical_FOV ());
	::Initialize_Spinner (m_HFOVSpin, hfov_deg, 0.0F, 180.0F);
	::Initialize_Spinner (m_VFOVSpin, vfov_deg, 0.0F, 180.0F);

	//
	//	Setup the camera lens controls
	//
	float hfov = camera->Get_Horizontal_FOV ();
	const float constant	= (18.0F / 1000.0F);
	float lens				= (constant / (::tan (hfov / 2))) * 1000.0F;
	::Initialize_Spinner (m_LensSpin, lens, 1.0F, 200.0F);

	OnFovCheck ();
	OnClipPlaneCheck ();
	return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnOK
//
/////////////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::OnOK (void)
{
	CW3DViewDoc *doc				= ::GetCurrentDocument ();
	CGraphicView *graphic_view = doc->GetGraphicView ();
	CameraClass *camera			= graphic_view->GetCamera ();

	bool manual_fov		= (SendDlgItemMessage (IDC_FOV_CHECK, BM_GETCHECK) == 1);
	bool manual_planes	= (SendDlgItemMessage (IDC_CLIP_PLANE_CHECK, BM_GETCHECK) == 1);

	doc->Set_Manual_FOV (manual_fov);
	doc->Set_Manul_Clip_Planes (manual_planes);
	if (manual_fov == false) {
		graphic_view->Reset_FOV ();
	} else {

		//
		//	Update the camera's FOV
		//
		float hfov_deg = ::GetDlgItemFloat (m_hWnd, IDC_HFOV_EDIT);
		float vfov_deg = ::GetDlgItemFloat (m_hWnd, IDC_VFOV_EDIT);
		camera->Set_View_Plane (DEG_TO_RAD (hfov_deg), DEG_TO_RAD (vfov_deg));
	}

	//
	//	Update the camera's clip planes
	//
	float znear = ::GetDlgItemFloat (m_hWnd, IDC_NEAR_CLIP_EDIT);
	float zfar = ::GetDlgItemFloat (m_hWnd, IDC_FAR_CLIP_EDIT);
	camera->Set_Clip_Planes (znear, zfar);
	doc->Save_Camera_Settings ();

	//
	// Update the fog settings. The fog near clip plane should always be equal
	// to the camera near clip plane, but the fog far clip plane is scene
	// dependant. We will be sure to modify only the near clip plane here.
	//
	float fog_near, fog_far;
	doc->GetScene()->Get_Fog_Range(&fog_near, &fog_far);
	doc->GetScene()->Set_Fog_Range(znear, fog_far);
	doc->GetScene()->Recalculate_Fog_Planes();
	
	//
	//	Refresh the camera settings
	//
	RenderObjClass *render_obj = doc->GetDisplayedObject ();
	if (render_obj != NULL) {
		graphic_view->Reset_Camera_To_Display_Object (*render_obj);
	}

	CDialog::OnOK ();
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnFovCheck
//
/////////////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::OnFovCheck (void) 
{
	bool manual_fov = (SendDlgItemMessage (IDC_FOV_CHECK, BM_GETCHECK) == 1);
	::EnableWindow (m_VFOVSpin, manual_fov);
	::EnableWindow (m_HFOVSpin, manual_fov);
	::EnableWindow (m_LensSpin, manual_fov);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_VFOV_EDIT), manual_fov);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_HFOV_EDIT), manual_fov);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_LENS_EDIT), manual_fov);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnClipPlaneCheck
//
/////////////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::OnClipPlaneCheck (void) 
{
	bool manual_planes = (SendDlgItemMessage (IDC_CLIP_PLANE_CHECK, BM_GETCHECK) == 1);
	::EnableWindow (m_NearClipSpin, manual_planes);
	::EnableWindow (m_FarClipSpin, manual_planes);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_NEAR_CLIP_EDIT), manual_planes);
	::EnableWindow (::GetDlgItem (m_hWnd, IDC_FAR_CLIP_EDIT), manual_planes);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnReset
//
/////////////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::OnReset (void)
{
	CW3DViewDoc *doc				= ::GetCurrentDocument ();
	CGraphicView *graphic_view = doc->GetGraphicView ();
	CameraClass *camera			= graphic_view->GetCamera ();

	doc->Set_Manual_FOV (false);
	doc->Set_Manul_Clip_Planes (false);
		
	graphic_view->Reset_FOV ();	
	RenderObjClass *render_obj = doc->GetDisplayedObject ();
	if (render_obj != NULL) {
		graphic_view->Reset_Camera_To_Display_Object (*render_obj);
	}

	//
	//	Update the clip plane controls
	//
	float znear = 0;
	float zfar = 0;
	camera->Get_Clip_Planes (znear, zfar);
	::SetDlgItemFloat (m_hWnd, IDC_NEAR_CLIP_EDIT, znear);
	::SetDlgItemFloat (m_hWnd, IDC_FAR_CLIP_EDIT, zfar);

	//
	//	Update the FOV controls
	//
	int hfov_deg = (int)RAD_TO_DEG (camera->Get_Horizontal_FOV ());
	int vfov_deg = (int)RAD_TO_DEG (camera->Get_Vertical_FOV ());
	::SetDlgItemFloat (m_hWnd, IDC_HFOV_EDIT, hfov_deg);
	::SetDlgItemFloat (m_hWnd, IDC_VFOV_EDIT, vfov_deg);
	
	//
	//	Setup the camera lens controls
	//
	float vfov = camera->Get_Vertical_FOV ();
	float lens = ((::atan ((18.0F / 1000.0F)) / vfov) * 2.0F) * 1000.0F;
	::SetDlgItemFloat (m_hWnd, IDC_LENS_EDIT, lens);
	return ;
}


////////////////////////////////////////////////////////////////////
//
//	OnNotify
//
////////////////////////////////////////////////////////////////////
BOOL
CameraSettingsDialogClass::OnNotify
(
	WPARAM wParam,
	LPARAM lParam,
	LRESULT *pResult
) 
{
	//
	//	Update the spinner control if necessary
	//
	NMHDR *header = (NMHDR *)lParam;
	if ((header != NULL) && (header->code == UDN_DELTAPOS)) {
		LPNMUPDOWN updown_info = (LPNMUPDOWN)lParam;		
		::Update_Spinner_Buddy (header->hwndFrom, updown_info->iDelta);

		//
		//	Update the FOV settings (they are dependent on each other)
		//
		if (updown_info->hdr.idFrom == IDC_LENS_SPIN) {
			Update_FOV ();
		} else if (updown_info->hdr.idFrom == IDC_HFOV_SPIN) {
			Update_Camera_Lens ();
		}
	}
	
	// Allow the base class to process this message
	return CDialog::OnNotify (wParam, lParam, pResult);
}


////////////////////////////////////////////////////////////////////
//
//	Update_Camera_Lens
//
////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::Update_Camera_Lens (void)
{
	//
	//	Get the current vertical FOV settings
	//
	float hfov = ::GetDlgItemFloat (m_hWnd, IDC_HFOV_EDIT);

	//
	//	Convert the vertical FOV to a camera lens setting
	//
	if (hfov > 0) {
		const float constant	= (18.0F / 1000.0F);
		float lens				= (constant / (::tan (DEG_TO_RAD (hfov) / 2))) * 1000.0F;
		::SetDlgItemFloat (m_hWnd, IDC_LENS_EDIT, lens);	
	}

	return ;
}


////////////////////////////////////////////////////////////////////
//
//	Update_FOV
//
////////////////////////////////////////////////////////////////////
void
CameraSettingsDialogClass::Update_FOV (void)
{
	//
	//	Get the current camera lens setting
	//
	float lens = (::GetDlgItemFloat (m_hWnd, IDC_LENS_EDIT) / 1000.0F);

	//
	//	Convert the camera lens to a FOV
	//
	if (lens > 0) {
		const float constant	= (18.0F / 1000.0F);
		float hfov				= (::atan (constant / lens) * 2.0F);
		float vfov				= (3 * hfov / 4);

		//
		//	Pass the new FOV settings onto the dialog
		//
		::SetDlgItemFloat (m_hWnd, IDC_HFOV_EDIT, RAD_TO_DEG (hfov));
		::SetDlgItemFloat (m_hWnd, IDC_VFOV_EDIT, RAD_TO_DEG (vfov));
	}
	return ;
}


////////////////////////////////////////////////////////////////////
//
//	Update_FOV
//
////////////////////////////////////////////////////////////////////
BOOL
CameraSettingsDialogClass::OnCommand
(
	WPARAM wParam,
	LPARAM lParam
) 
{
	static bool updating = false;
	if (updating == false) {

		//
		//	Update the FOV settings if necessary
		//
		if (	LOWORD (wParam) == IDC_HFOV_EDIT &&
				HIWORD (wParam) == EN_UPDATE)
		{
			updating = true;
			Update_Camera_Lens ();
			updating = false;
		} else if (	LOWORD (wParam) == IDC_LENS_EDIT &&
						HIWORD (wParam) == EN_UPDATE)
		{
			updating = true;
			Update_FOV ();
			updating = false;
		}
	}

	return CDialog::OnCommand(wParam, lParam);
}
