//================================================================
// Show/hide things
//
// Plugin for EQUINOX-3D
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Apr-24 21:44:17
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#include <stdio.h>

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

#include <Xe/Label.h>
#include <Xe/PushButton.h>

#include <E3D/E3D.h>

#include <E3D/UI/Panel.h>


static XeDWidget          _MenuButtons[16];static int  _MenuButtons_Count = 0;
static EScriptFunction*  _ScriptFunctions[32];static int  _ScriptFunctions_Count = 0;


// Operations
//
enum
{
	CR_HIDE_TOGGLE_SELECTED = 1,
	CR_HIDE_SELECTED,
	CR_HIDE_UNSELECTED,
	CR_UNHIDE_SELECTED,
	CR_UNHIDE_ALL
};


#define M_ObjectGetChangeFlagsByClass(pObject) do\
{\
	if(EObject_IsSubClass(pObject, E3dGeometry))\
	{\
		if(EObject_IsSubClass(pObject, E3dPointSet))\
		{\
			LChanged |= E3dChgPointSetALL;\
		}\
		else if(EObject_IsSubClass(pObject, E3dSpline))\
		{\
			LChanged |= E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY;\
		}\
		else if(EObject_IsSubClass(pObject, E3dMesh))\
		{\
			E3dMesh*	lMesh = (E3dMesh*)pObject;\
\
			if(lMesh->PolyGroups.Count > 0) LChanged |= E3dChgMeshSHAPE;\
			else LChanged |= E3dChgWIREFRAME;\
		}\
		else if(EObject_IsSubClass(pObject, E3dFace))\
		{\
			LChanged |= E3dChgFACE;\
		}\
	}\
	else if(EObject_IsSubClass(LObject, E3dLight)) LChanged |= E3dChgLIGHT;\
	else if(EObject_IsSubClass(LObject, E3dCamera))\
	{\
		E3dCamera*	lCamera = (E3dCamera*)pObject;\
\
		if(E3dp_Main3DWindow && lCamera == E3dp_Main3DWindow->PerspectiveView->VCamera) LChanged |= E3dChgCAMERA;\
	}\
} while(0)


static EChangeFlags _Model_GetChangeFlagsByObjects(E3dModel* PModel)
{
	EChangeFlags	LChanged = 0;

	EIndex  LObjI = 0;const EIndex  LObjN = PModel->Objects.Count;E3dObject**   LObjects = PModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
	{
		E3dObject*	LObject = LObjects[LObjI];

		M_ObjectGetChangeFlagsByClass(LObject);
	}

	return(LChanged);
}


//========================================
// Object display/hide
//========================================
static EChangeFlags _HideToggleSelection(E3dScene* PScene)
{
	EIndex        LNodesHid = 0, LNodesUnhid = 0,
	              LObjectsHid = 0, LObjectsUnhid = 0,
	              LPolyGroupsHid = 0, LPolyGroupsUnhid = 0;
	EChangeFlags  LChanged = 0;

	EIndex  LRI = 0;const EIndex  LRN = PScene->RootNodes.Count;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LNode = PScene->RootNodes.Items[LRI];for(;LNode;LNode = LNode->Next)
		{
			switch(LNode->Selection)
			{
				case E3dNodeSelNODE:
				case E3dNodeSelBRANCH:
				case E3dNodeSelBRANCH_ROOT:
					if(LNode->Visible)
					{
						LNode->Visible = FALSE;LNodesHid++;
					}
					else
					{
						LNode->Visible = TRUE;LNodesUnhid++;
					}

					{
						E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);
						if(LModel) LChanged |= _Model_GetChangeFlagsByObjects(LModel);
					}
				break;
			}
		}
	}


	{
		EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LNSelectedObjects = E3dScene_GetObjects(PScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE,  &LSelectedObjects);if(LNSelectedObjects > 0)
		{

			EIndex  LI = 0;for(;LI < LNSelectedObjects;LI++)
			{
				E3dObject*  LObject = LSelectedObjects.Items[LI];

				if(EObject_IsSubClass(LObject, E3dMesh))
				{
					if(LObject->Selection == E3dNodeSelOBJECT)
					{
						if(LObject->Visible)
						{
							LObject->Visible = FALSE;LObjectsHid++;
						}
						else
						{
							LObject->Visible = TRUE;LObjectsUnhid++;
						}
						M_ObjectGetChangeFlagsByClass(LObject);
					}
					else
					{
						E3dMesh*        LMesh = (E3dMesh*)LObject;
						E3dPolyGroup**  LPolyGroups = LMesh->PolyGroups.Items;
						EIndex   LGCnt = 0;const EIndex  LGNum = LMesh->PolyGroups.Count;for(;LGCnt < LGNum;LGCnt++)
						{
							E3dPolyGroup*  LPolyGroup = LPolyGroups[LGCnt];

							if(LPolyGroup->Selected)
							{
								if(LPolyGroup->Visible)
								{
									LPolyGroup->Visible = FALSE;LPolyGroupsHid++;
								}
								else
								{
									LPolyGroup->Visible = TRUE;LPolyGroupsUnhid++;
								}
								M_ObjectGetChangeFlagsByClass(LObject);
							}
						}
					}
				}
				else
				{
					if(LObject->Selection == E3dNodeSelOBJECT)
					{
						if(LObject->Visible)
						{
							LObject->Visible = FALSE;LObjectsHid++;
						}
						else
						{
							LObject->Visible = TRUE;LObjectsUnhid++;
						}
						M_ObjectGetChangeFlagsByClass(LObject);
					}
				}
			}
			EList_FreeA_s(LSelectedObjects);
		}
	}

	if(LNodesHid > 0 || LNodesUnhid > 0 || LObjectsHid || LObjectsUnhid || LPolyGroupsHid || LPolyGroupsUnhid)
	{
		ECallbackStruct  LCBS;ECallbackStruct_Init(&LCBS, NULL,  E3dCR_HIDE_UNHIDE);
		E3d_CallCallbacks((ECoreObject*)PScene,  PScene->Callbacks, PScene->Callbacks_Count,  &LCBS);
	}

	return(LChanged);
}


//========================================
// Object display/hide
//========================================
static EChangeFlags _HideUnhide(E3dScene* PScene, const int POperation)
{
	EIndex        LNodesHid = 0, LNodesUnhid = 0,
	              LObjectsHid = 0, LObjectsUnhid = 0,
	              LPolyGroupsHid = 0, LPolyGroupsUnhid = 0;
	EChangeFlags  LChanged = 0;

	EIndex  LRI = 0;const EIndex  LRN = PScene->RootNodes.Count;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LNode = PScene->RootNodes.Items[LRI];for(;LNode;LNode = LNode->Next)
		{
			switch(POperation)
			{
				case CR_HIDE_SELECTED:
					switch(LNode->Selection)
					{
						case E3dNodeSelNODE:
						case E3dNodeSelBRANCH:
						case E3dNodeSelBRANCH_ROOT:
							if(LNode->Visible)
							{
								LNode->Visible = FALSE;LNodesHid++;

								{
									E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);
									if(LModel) LChanged |= _Model_GetChangeFlagsByObjects(LModel);
								}
							}
						break;

						default:
							{
								E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
								{
									EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
									{
										E3dObject*  LObject = LObjects[LObjI];

										if(LObject->Selection == E3dNodeSelOBJECT)
										{
											if(LObject->Visible)
											{
												LObject->Visible = FALSE;LObjectsHid++;M_ObjectGetChangeFlagsByClass(LObject);
											}
										}
										else
										{
											if(EObject_IsSubClass(LObject, E3dMesh))
											{
												E3dMesh*        LMesh = (E3dMesh*)LObject;
												E3dPolyGroup**  LPolyGroups = LMesh->PolyGroups.Items;
												EIndex  LGC = 0;const EIndex  LGN = LMesh->PolyGroups.Count;for(;LGC < LGN;LGC++)
												{
													E3dPolyGroup*  LPolyGroup = LPolyGroups[LGC];if(LPolyGroup->Selected)
													{
														if(LPolyGroup->Visible)
														{
															LPolyGroup->Visible = FALSE;LPolyGroupsHid++;M_ObjectGetChangeFlagsByClass(LObject);
														}
													}
												}
											}
										}
									}
								}
							}
						break;
					}
				break;

				case CR_HIDE_UNSELECTED:
					switch(LNode->Selection)
					{
						case E3dNodeSelNONE:
							if(LNode->Visible)
							{
								LNode->Visible = FALSE;LNodesHid++;

								{
									E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);
									if(LModel) LChanged |= _Model_GetChangeFlagsByObjects(LModel);
								}
							}
						break;

						case E3dNodeSelOBJECT:
							{
								E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
								{
									EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
									{
										E3dObject*  LObject = LObjects[LObjI];

										switch(LObject->Selection)
										{
											case E3dObjectSelNONE:
												if(LObject->Visible)
												{
													LObject->Visible = FALSE;LObjectsHid++;M_ObjectGetChangeFlagsByClass(LObject);
												}
											break;

											case E3dObjectSelPOLYGROUP:
												if(EObject_IsSubClass(LObject, E3dMesh))
												{
													E3dMesh*        LMesh = (E3dMesh*)LObject;
													E3dPolyGroup**  LPolyGroups = LMesh->PolyGroups.Items;

													EIndex  LPGC = 0;const EIndex  LPGN = LMesh->PolyGroups.Count;for(;LPGC < LPGN;LPGC++)
													{
														E3dPolyGroup*  LPolyGroup = LPolyGroups[LPGC];

														if(!LPolyGroup->Selected)
														{
															if(LPolyGroup->Visible)
															{
																LPolyGroup->Visible = FALSE;LPolyGroupsHid++;M_ObjectGetChangeFlagsByClass(LObject);
															}
														}
													}
												}
											break;
										}
									}
								}
							}
						break;
					}
				break;

				case CR_UNHIDE_SELECTED:
					if(LNode->Selection != E3dNodeSelNONE)
					{
						E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);

						const EBool  LNodeSelected = E3dNode_IsSelected(LNode);
						if(LNodeSelected && !LNode->Visible)
						{
							LNode->Visible = TRUE;LNodesUnhid++;

							if(LModel) LChanged |= _Model_GetChangeFlagsByObjects(LModel);
						}

						if(LModel)
						{
							EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
							{
								E3dObject*  LObject = LObjects[LObjI];

								if(LNodeSelected || LObject->Selection)
								{
									if(!LObject->Visible)
									{
										LObject->Visible = TRUE;LObjectsUnhid++;M_ObjectGetChangeFlagsByClass(LObject);
									}

// If all Nodes of this Object are hidden, un-hide the first one
//
									if(LObject->Models.Count > 0)
									{
										EIndex  LI = 0;const EIndex  LN = LObject->Models.Count;for(;LI < LN;LI++)
										{
											if(LObject->Models.Items[LI]->Visible) break;
										}
										if(LI == LObject->Models.Count) LObject->Models.Items[0]->Visible = TRUE;
									}

									if(EObject_IsSubClass(LObject, E3dMesh))
									{
										E3dMesh*        LMesh = (E3dMesh*)LObject;
										E3dPolyGroup**  LPolyGroups = LMesh->PolyGroups.Items;
										EIndex  LGCnt = 0;const EIndex  LGNum = LMesh->PolyGroups.Count;for(;LGCnt < LGNum;LGCnt++)
										{
											E3dPolyGroup*  LPolyGroup = LPolyGroups[LGCnt];

											if(LObject->Selection == E3dObjectSelPOLYGROUP)
											{
												if(LPolyGroup->Selected)
												{
													if(!LPolyGroup->Visible)
													{
														LPolyGroup->Visible = TRUE;LPolyGroupsUnhid++;M_ObjectGetChangeFlagsByClass(LObject);
													}
												}
											}
											else if(!LPolyGroup->Visible)
											{
												LPolyGroup->Visible = TRUE;LPolyGroupsUnhid++;M_ObjectGetChangeFlagsByClass(LObject);
											}
										}
									}
								}
							}
						}
					}
				break;

				case CR_UNHIDE_ALL:
					{
						E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);

						if(!LNode->Visible)
						{
							LNode->Visible = TRUE;LNodesUnhid++;

							if(LModel) LChanged |= _Model_GetChangeFlagsByObjects(LModel);
						}

						if(LModel)
						{
							EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
							{
								E3dObject*  LObject = LObjects[LObjI];

								if(!LObject->Visible)
								{
									LObject->Visible = TRUE;LObjectsUnhid++;M_ObjectGetChangeFlagsByClass(LObject);
								}

								if(EObject_IsSubClass(LObject, E3dMesh))
								{
									E3dMesh*        LMesh = (E3dMesh*)LObject;
									E3dPolyGroup**  LPolyGroups = LMesh->PolyGroups.Items;
									EIndex  LGCnt = 0;const EIndex  LGNum = LMesh->PolyGroups.Count;for(;LGCnt < LGNum;LGCnt++)
									{
										E3dPolyGroup*  LPolyGroup = LPolyGroups[LGCnt];if(!LPolyGroup->Visible)
										{
											LPolyGroup->Visible = TRUE;LPolyGroupsUnhid++;
											M_ObjectGetChangeFlagsByClass(LObject);
										}
									}
								}
							}
						}
					}
				break;
			}
		}
	}

	if(LNodesHid > 0 || LNodesUnhid > 0 || LObjectsHid || LObjectsUnhid || LPolyGroupsHid || LPolyGroupsUnhid)
	{
		ECallbackStruct  LCBS;ECallbackStruct_Init(&LCBS, NULL,  E3dCR_HIDE_UNHIDE);
		E3d_CallCallbacks((ECoreObject*)PScene,  PScene->Callbacks, PScene->Callbacks_Count,  &LCBS);
	}

	return(LChanged);
}


//========================================
// Menu callback
//========================================
static void _MCB_ModelDisplay(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	EChangeFlags  LChanged = 0;

	E3dScene*  LScene = E3d_Scene;

	switch((EPointerInt)PClientData)
	{
		case CR_HIDE_TOGGLE_SELECTED:  LChanged = _HideToggleSelection(LScene);break;
		default:                       LChanged = _HideUnhide(LScene, (EPointerInt)PClientData);break;
	}

//{ char	s[256];printf("%s() [%s]\n", __FUNCTION__, E3dSceneChangeFlags_ToString(LChanged, EfCOLOR,  s));fflush(stdout); }
	if(LChanged) E3dWindows_RedrawByChangeFlags(LScene, LChanged | E3dChgSCHEMATICS);
}


static void _MCB_ToggleHideSelectedEdges(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EIndex     LTotalEdgesChanged = 0;

	EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LNSelectedObjects = E3dScene_GetObjects(LScene, E3dItemMESH, E3dSELECTED, FALSE,  &LSelectedObjects);if(LNSelectedObjects > 0)
	{
		EIndex  LI = 0;for(;LI < LNSelectedObjects;LI++)
		{
			E3dMesh*  LMesh = (E3dMesh*)LSelectedObjects.Items[LI];
			EIndex    LMeshEdgesChanged = 0;
			EIndex  LEC = 0;const EIndex  LEN = LMesh->Edges.Count;E3dEdge*  LEdge = LMesh->Edges.Items;for(;LEC < LEN;LEC++, LEdge++)
			{
				if(LEdge->Flags & E3dEdgeSELECTED)
				{
					if(LEdge->Flags & E3dEdgeVISIBLE) LEdge->Flags &= ~E3dEdgeVISIBLE;
					else LEdge->Flags |= E3dEdgeVISIBLE;
					LMeshEdgesChanged++;
				}
			}
			if(LMeshEdgesChanged)
			{
				E3dMesh_EdgeFlagsUpdate(LMesh);
				LTotalEdgesChanged += LMeshEdgesChanged;
			}
		}
		EList_FreeA_s(LSelectedObjects);
	}

	if(LTotalEdgesChanged) E3dWindows_RedrawByChangeFlags(LScene, E3dChgWIREFRAME);
}


static void _MCB_HideSelectedEdges(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EIndex     LTotalEdgesChanged = 0;

	EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LNSelectedObjects = E3dScene_GetObjects(LScene, E3dItemMESH, E3dSELECTED, FALSE,  &LSelectedObjects);if(LNSelectedObjects > 0)
	{
		EIndex  LI = 0;for(;LI < LNSelectedObjects;LI++)
		{
			E3dMesh*  LMesh = (E3dMesh*)LSelectedObjects.Items[LI];
			EIndex    LMeshEdgesChanged = 0;
			EIndex  LEC = 0;const EIndex  LEN = LMesh->Edges.Count;E3dEdge*  LEdge = LMesh->Edges.Items;for(;LEC < LEN;LEC++, LEdge++)
			{
				if(LEdge->Flags & E3dEdgeSELECTED)
				{
					if(LEdge->Flags & E3dEdgeVISIBLE)
					{
						LEdge->Flags &= ~E3dEdgeVISIBLE;
						LMeshEdgesChanged++;
					}
				}
			}
			if(LMeshEdgesChanged)
			{
				E3dMesh_EdgeFlagsUpdate(LMesh);
				LTotalEdgesChanged += LMeshEdgesChanged;
			}
		}
		EList_FreeA_s(LSelectedObjects);
	}

	if(LTotalEdgesChanged) E3dWindows_RedrawByChangeFlags(LScene, E3dChgWIREFRAME);
}


static int _Sc_HideSelectedPoints(EPointer PParams)
{
	EIndex  LTotalPointsChanged = 0;

	EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LNSelectedObjects = E3dScene_GetObjects(E3d_Scene, E3dItemMESH|E3dItemSPLINE, E3dSELECTED, FALSE,  &LSelectedObjects);if(LNSelectedObjects > 0)
	{
		EIndex  LI = 0;for(;LI < LNSelectedObjects;LI++)
		{
			E3dObject*  LObject = LSelectedObjects.Items[LI];if(EObject_IsSubClass(LObject, E3dMesh))
			{
				E3dMesh*  LMesh = (E3dMesh*)LObject;
				EIndex    LPointsChanged = 0;
				EIndex  LVC = 0;const EIndex  LVN = LMesh->Vertices.Count;E3dVertex*  LVertex = LMesh->Vertices.Items;const E3dVertexSize  LVertexSize = LMesh->VertexSize;for(;LVC < LVN;LVC++, EStruct_Inc(LVertex, LVertexSize))
				{
					if(LVertex->Flags & E3dVtxSELECTED)
					{
						if(LVertex->Flags & E3dVtxVISIBLE)
						{
							LVertex->Flags &= ~E3dVtxVISIBLE;
							LPointsChanged++;
						}
					}
				}
				LTotalPointsChanged += LPointsChanged;
			}
			else if(EObject_IsSubClass(LObject, E3dSpline))
			{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
			}
		}
		EList_FreeA_s(LSelectedObjects);
	}

	if(LTotalPointsChanged) return(E_SUCCESS);
	else return(E_NOTHING_TO_DO);
}


static void _MCB_HideSelectedPoints(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*     LScene = E3d_Scene;
	EChangeFlags  LSceneChangeFlags = _Sc_HideSelectedPoints(NULL);
	if(LSceneChangeFlags == E_SUCCESS) E3dWindows_RedrawByChangeFlags(LScene, LSceneChangeFlags);
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Toggle hide/un-hide selected", '\0', NULL, NULL, FALSE, NULL, _MCB_ModelDisplay, (EPointer)CR_HIDE_TOGGLE_SELECTED);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Hide selected",       '\0', NULL, NULL, FALSE, NULL, _MCB_ModelDisplay, (EPointer)CR_HIDE_SELECTED);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Hide unselected",     '\0', NULL, NULL, FALSE, NULL, _MCB_ModelDisplay, (EPointer)CR_HIDE_UNSELECTED);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Un-hide selected",    '\0', NULL, NULL, FALSE, NULL, _MCB_ModelDisplay, (EPointer)CR_UNHIDE_SELECTED);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Un-hide all",         '\0', NULL, NULL, FALSE, NULL, _MCB_ModelDisplay, (EPointer)CR_UNHIDE_ALL);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Toggle hide/un-hide selected Edges", '\0', NULL, NULL, FALSE, NULL, _MCB_ToggleHideSelectedEdges, (EPointer)0);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Hide selected Edges",                '\0', NULL, NULL, FALSE, NULL, _MCB_HideSelectedEdges, (EPointer)0);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Display", "Hide selected Points",               '\0', NULL, NULL, FALSE, NULL, _MCB_HideSelectedPoints, (EPointer)0);
	E3dScriptFunction_Register("HideSelectedPoints", "Hide selected points", "Hide selected Vertices on selected Meshes.",  _Sc_HideSelectedPoints, NULL,  _ScriptFunctions + _ScriptFunctions_Count++);

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		{ int  LI = 0;for(;LI < _MenuButtons_Count;LI++) { XeItemFree(_MenuButtons[LI]); } _MenuButtons_Count = 0;  }
		{ int  LI = 0;for(;LI < _ScriptFunctions_Count;LI++) { E3dScriptFunction_Remove(_ScriptFunctions[LI]); } _ScriptFunctions_Count = 0; }
	}

	return(E_SUCCESS);
}
