//================================================================================================
// Plugin for EQUINOX-3D
//
// Area lights
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Aug-02 01:03:04
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================================
#include <assert.h>
#include <stdio.h>

#include <ELists.h>
#include <EMalloc.h>
#include <EPlugins.h>

#include <E3D/Scene.h>

#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>
#include <E3D/UI/StatusPanel.h>

char*  E_PluginInfo = "Create different types of area lights: environment sphere, rectangular, Sun, etc.\nEQUINOX-3D can use any Mesh as an area light. These tools are just for simplyfing the creation of common light types.";


EpIconDef3I(static, ALightRectangle);
EpIconDef3I(static, ALightSphereEnv);
EpIconDef3I(static, ALightSun);

static EPlugin*  _Plugin = NULL;

static XeDWidget  _ToolBarButtons[16];static int  _NumOfToolBarButtons = 0;


//================================================
// ToolBar icon callback handler
//================================================
static void _CB_Rectangle(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
// Create customized Grid object
//
	E3dObjectComponentClass*  LClass = E3dObjectComponent_ClassByName("Grid");if(LClass)
	{
		E3dObjectComponent*  LInfo = E3dObjectComponent_New(LClass);if(LInfo)
		{
			E3dScene*     LScene = E3d_Scene;
			EChangeFlags  LChanged = 0;

			EObject_NamedResourceSet1f(LInfo, "UEdgeLength", 1.0);
			EObject_NamedResourceSet1f(LInfo, "VEdgeLength", 1.0);
			E3dModel*  LModel = E3dModel_CustomObjectNewInit(LScene, LInfo, NULL, NULL, (E3dp_Prefs.Duplicate_Flags & E3dCopyMODEL_UNIQUE_NAME) != 0);

			EObject_SetName(LModel, EStrDup("ALight"), FALSE);
			LModel->TransformSet.Translation.Y = 2.0;
			LModel->TransformSet.EulerRotation.Angles.Z = 180.0;
			E3dNodeHrc_UpdateMatrices((E3dNode*)LModel, TRUE);
			E3dScene_NodeHrcAdd(LScene, (E3dNode*)LModel, E3dCallSCENE_CALLBACKS);
			LChanged |= E3dModel_GetChangeFlagsByObjects(LModel)|E3dChgHIERARCHY;

			E3dObjectComponent_Free(LInfo);

			if(LModel->Objects.Count > 0)
			{
				E3dObject*  LObject = LModel->Objects.Items[0];
				E3dMesh*    LMesh = E_DynamicCast(LObject, E3dMesh);if(LMesh)
				{
					LMesh->CastsShadows = FALSE;LMesh->ReceivesShadows = FALSE;
					EObject_SetName(LMesh, EStrDup("aLight"), FALSE);

					LModel->Selection = E3dNodeSelOBJECT;LMesh->Selection = E3dObjectSelOBJECT;
					if(LMesh->PolyGroups.Count > 0)
					{
						E3dPolyGroup*  LPolyGroup = LMesh->PolyGroups.Items[0];

						E3dMaterial*  LMaterial = E3dMaterial_New(E3dUnlitShader_class);

						EObject_SetName(LMaterial, EStrDup("Light"), FALSE);
						E3dShader*	LShader = LMaterial->Shader;
						EObject_NamedResourceSetColorRGBf(LShader, "Diffuse",    0.0,  0.0,  0.0);
						EObject_NamedResourceSetColorRGBf(LShader, "Emission",  10.0, 10.0, 10.0);

						E_AssignToMember(LMaterial, LPolyGroup,Material);
						E3dScene_MaterialAdd(LScene, LMaterial);
					}

					E3dNodeHrc_UpdateMaterialInheritance((E3dNode*)LModel);
					E3dScene_ObjectsUpdateModelList(LScene);
					E3dScene_LayoutOnSchematics(LScene, TRUE);

					E3dWindows_RedrawByChangeFlags(LScene, LChanged);
					if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) E3dObject_Edit(LObject, LClass);
				}
			}
		}
	}
}


//================================================
// Create spherical environment light
//================================================
static void _CB_Sphere(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectComponentClass*  LClass = E3dObjectComponent_ClassByName("Sphere");if(LClass)
	{
		E3dObjectComponent*  LInfo = E3dObjectComponent_New(LClass);if(LInfo)
		{
			E3dScene*     LScene = E3d_Scene;
			EChangeFlags  LChanged = 0;

			EObject_NamedResourceSet1f(LInfo, "Radius", 100.0);
			EObject_NamedResourceSet1i(LInfo, "USections", 8);
			EObject_NamedResourceSet1i(LInfo, "VSections", 6);
//			EObject_NamedResourceSet1f(LInfo, "NorthMostLatitude", 90.0);
//			EObject_NamedResourceSet1f(LInfo, "SouthMostLatitude", 88.0);
			EObject_NamedResourceSet1b(LInfo, "InsideOut", TRUE);
			E3dModel*  LModel = E3dModel_CustomObjectNewInit(LScene, LInfo, NULL, NULL, (E3dp_Prefs.Duplicate_Flags & E3dCopyMODEL_UNIQUE_NAME) != 0);

			EObject_SetName(LModel, EStrDup("EnvSphere"), FALSE);

			E3dNodeHrc_UpdateMatrices((E3dNode*)LModel, TRUE);
			E3dScene_NodeHrcAdd(LScene, (E3dNode*)LModel, E3dCallSCENE_CALLBACKS);
			LChanged |= E3dModel_GetChangeFlagsByObjects(LModel)|E3dChgHIERARCHY;

			E3dObjectComponent_Free(LInfo);

			if(LModel->Objects.Count > 0)
			{
				E3dObject*  LObject = LModel->Objects.Items[0];
				E3dMesh*    LMesh = E_DynamicCast(LObject, E3dMesh);if(LMesh)
				{
					LMesh->CastsShadows = FALSE;LMesh->ReceivesShadows = FALSE;
					EObject_SetName(LMesh, EStrDup("EnvSphere"), FALSE);

					LModel->Selection = E3dNodeSelOBJECT;LMesh->Selection = E3dObjectSelOBJECT;
					if(LMesh->PolyGroups.Count > 0)
					{
						E3dPolyGroup*  LPolyGroup = LMesh->PolyGroups.Items[0];

// Add a "Spherical" texture mapper, in case the user wants to add an IBL image
//
						{
							E3dTextureMapper*  LTextureMapper = E3dTextureMapper_New(E3dTextureMapperSpherical_class);
							EList_ObjectAdd_s(LPolyGroup->TextureMappers, LTextureMapper);
							const E3dTextureMapperIndex  LMapperIndex = LPolyGroup->TextureMappers.Count - 1;
							E3dPolyGroup_VertexAttributeAdd(LPolyGroup,  "Spherical", E3dVtxAttribute_ST, LMapperIndex,  NULL);  // Make sure there are corresponding STs on the PolyGroup
							E3dPolyGroup_Map2DTextureByMapper((E3dVertexGeometry*)LMesh, LPolyGroup, LTextureMapper);

							ENew(LPolyGroup->TextureMapperRefs.Items);E_AssignNoChkNoFree(LTextureMapper, LPolyGroup->TextureMapperRefs.Items[0]);
							LPolyGroup->TextureMapperRefs.Count = 1;
						}

						EObject_SetName(LPolyGroup, EStrDup("EnvSpherePG"), FALSE);

						E3dMaterial*  LMaterial = E3dMaterial_New(E3dUnlitShader_class);
						E_AssignToMember(LMaterial, LPolyGroup,Material);

						EObject_SetName(LMaterial, EStrDup("Env"), FALSE);
						E3dShader*    LShader = LMaterial->Shader;
						EObject_NamedResourceSetColorRGBf(LShader, "Diffuse",  0.0, 0.0, 0.0);
						EObject_NamedResourceSetColorRGBf(LShader, "Emission",  1.0, 1.0, 1.0);
						LMaterial->Light.NumOfSamples = 8 * 8;
						E3dScene_MaterialAdd(LScene, LMaterial);
					}

					E3dNodeHrc_UpdateMaterialInheritance((E3dNode*)LModel);
					E3dScene_ObjectsUpdateModelList(LScene);
					E3dScene_LayoutOnSchematics(LScene, TRUE);

					E3dWindows_RedrawByChangeFlags(LScene, LChanged);
					if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) E3dObject_Edit(LObject, LClass);
				}
			}
		}
	}
}


//================================================
// ToolBar icon callback handler
//================================================
static void _CB_Sun(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
// Create customized Grid object
//
	E3dObjectComponentClass*  LClass = E3dObjectComponent_ClassByName("Sphere");if(LClass)
	{
		E3dObjectComponent*  LInfo = E3dObjectComponent_New(LClass);if(LInfo)
		{
			E3dScene*     LScene = E3d_Scene;
			E3dModel*     LRootModel;E_New(E3dModel, LRootModel);EObject_SetName(LRootModel, EStrDup("SunPivot"), FALSE);LRootModel->TransformSet.EulerRotation.Angles = E3dCoord3_s(0.0, -45.0, 150.0);
			EChangeFlags  LChanged = 0;

			EObject_NamedResourceSet1f(LInfo, "Radius", 100.0);
			EObject_NamedResourceSet1i(LInfo, "USections", 8);
			EObject_NamedResourceSet1i(LInfo, "VSections", 6);
			EObject_NamedResourceSet1f(LInfo, "NorthMostLatitude", 90.0);
			EObject_NamedResourceSet1f(LInfo, "SouthMostLatitude", 88.0);
			EObject_NamedResourceSet1i8(LInfo, "Origin at", E3dBOTTOM);
			E3dModel*  LModel = E3dModel_CustomObjectNewInit(LScene,LInfo, NULL, NULL, (E3dp_Prefs.Duplicate_Flags & E3dCopyMODEL_UNIQUE_NAME) != 0);
			LModel->TransformSet.EulerRotation.Angles.Z = 90.0;

			E3dNode_ConnectChild((E3dNode*)LRootModel, (E3dNode*)LModel, ELstEND, FALSE, FALSE);

			EObject_SetName(LModel, EStrDup("Sun"), FALSE);
			LModel->TransformSet.Translation.X = 200.0;

			E3dNodeHrc_UpdateConnections((E3dNode*)LRootModel);
			E3dNodeHrc_UpdateMatrices((E3dNode*)LRootModel, TRUE);
			E3dScene_NodeHrcAdd(LScene, (E3dNode*)LRootModel, E3dCallSCENE_CALLBACKS);
			LChanged |= E3dModel_GetChangeFlagsByObjects(LModel)|E3dChgHIERARCHY;

			E3dObjectComponent_Free(LInfo);

			if(LModel->Objects.Count > 0)
			{
				E3dObject*  LObject = LModel->Objects.Items[0];
				E3dMesh*    LMesh = E_DynamicCast(LObject, E3dMesh);if(LMesh)
				{
					EObject_SetName(LMesh, EStrDup("Sun"), FALSE);

					LModel->Selection = E3dNodeSelOBJECT;LMesh->Selection = E3dObjectSelOBJECT;
					if(LMesh->PolyGroups.Count > 0)
					{
						E3dPolyGroup*  LPolyGroup = LMesh->PolyGroups.Items[0];

						E3dMaterial*  LMaterial = E3dMaterial_New(E3dUnlitShader_class);
						EObject_SetName(LMaterial, EStrDup("Sun"), FALSE);

						E3dShader*    LShader = LMaterial->Shader;
						EObject_NamedResourceSetColorRGBf(LShader, "Diffuse",      0.0,    0.0,    0.0);
						EObject_NamedResourceSetColorRGBf(LShader, "Emission",  5000.0, 5000.0, 5000.0);
						LMaterial->Light.NumOfSamples = 256 * 256;

						E_AssignToMember(LMaterial, LPolyGroup,Material);
						E3dScene_MaterialAdd(LScene, LMaterial);
					}

					E3dNodeHrc_UpdateMaterialInheritance((E3dNode*)LModel);
					E3dScene_ObjectsUpdateModelList(LScene);
					E3dScene_LayoutOnSchematics(LScene, TRUE);

					E3dWindows_RedrawByChangeFlags(LScene, LChanged);
					if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) E3dObject_Edit(LObject, LClass);
				}
			}
		}
	}
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

// Use default plugin icon
//
// XePushButton_AddImg(_ToolBarButtons[_NumOfToolBarButtons++], "Tool->Light", Plugin, "Rectangular area light", '\0', NULL, NULL, FALSE, "Create rectangular area light", _CB_Rectangle, (EPointer)0);

	EpM_CreateIconStates3_sp(ALightRectangle);XePushButton_AddImg3(_ToolBarButtons[_NumOfToolBarButtons++], ALightRectangle, "Tool->Light", "Rectangular area light", '\0', NULL, NULL, TRUE, "Create rectangular area light.\nRight button: dialog box for this tool", _CB_Rectangle, (EPointer)0);
	EpM_CreateIconStates3_sp(ALightSphereEnv);XePushButton_AddImg3(_ToolBarButtons[_NumOfToolBarButtons++], ALightSphereEnv, "Tool->Light", "Environment sphere",     '\0', NULL, NULL, TRUE, "Create a sphere area light, to produce a soft ambient light environment.\nYou can also add an emission texture to it, in the Material editor (image-based lighting = IBL).\nIf you do that, make sure to choose \"Spherical\" texture mapping, and set the \"Cutoff angle\" to 0.\nRight button: dialog box for this tool", _CB_Sphere, (EPointer)0);
	EpM_CreateIconStates3_sp(ALightSun);      XePushButton_AddImg3(_ToolBarButtons[_NumOfToolBarButtons++], ALightSun,       "Tool->Light", "Sun",                    '\0', NULL, NULL, TRUE, "Create a partial-sphere area light with a pivot at the origin, to simulate the Sun.\nRight button: dialog box for this tool", _CB_Sun,    (EPointer)0);

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		int  LC = 0;for(;LC < _NumOfToolBarButtons;LC++) { XeItemFree(_ToolBarButtons[LC]); } _NumOfToolBarButtons = 0;
	}

	EpIconFree3(ALightRectangle);
	EpIconFree3(ALightSphereEnv);
	EpIconFree3(ALightSun);

	return(E_SUCCESS);
}
