//================================================================
// EQUINOX-3D plugin
//
// Create a grid and change it interactively
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Mar-12 19:03:26
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#include <math.h>
#include <stdio.h>

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

#include <Xe/Matrix.h>
#include <Xe/Scale.h>
#include <Xe/Separator.h>
#include <Xe/ToggleButton.h>

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


static EPlugin*  _Plugin = NULL;

enum
{
	E3dPO_XY = 0,
	E3dPO_XZ
};


typedef struct
{
	XeWidget  HorizontalBW, CenterAtMinBW, SmoothBW,
	          TrianglesBW, BackSideBW, SidesBW,
	          UEdgeLengthScaleW, VEdgeLengthScaleW, ThicknessScaleW,
	          USubdivisionsScaleW, VSubdivisionsScaleW,
	          UCurvatureScaleW, VCurvatureScaleW;
} E3dResourcesGUI;


static XeDWidget  _MenuButton = NULL, _ToolBarButton = NULL;

EpIconDef3I(static, Grid);


typedef struct
{
	E3dObjectComponentCore;

// E3dGridInfo part
//
	E3dCoordinate  UCurvature, VCurvature;
	E3dCoordinate  UEdgeLength, VEdgeLength, Thickness;
	int            USubdivisions, VSubdivisions, Orientation;
	char           UCenterAt, VCenterAt;
	EBool          Smooth, BackSide, Sides, Triangles;
} E3dGridInfo;


#define ROffset(field)  (void*)(EPtrOffset(E3dGridInfo, field))
static EResource  _Resources[]=
{
	{ "UCurvature",     E3dCOORDINATE_TYPE,  ROffset(UCurvature) },
	{ "VCurvature",     E3dCOORDINATE_TYPE,  ROffset(VCurvature) },
	{ "UEdgeLength",    E3dCOORDINATE_TYPE,  ROffset(UEdgeLength) },
	{ "VEdgeLength",    E3dCOORDINATE_TYPE,  ROffset(VEdgeLength) },
	{ "Thickness",      E3dCOORDINATE_TYPE,  ROffset(Thickness) },

	{ "USubdivisions",  EResINT32,           ROffset(USubdivisions) },
	{ "VSubdivisions",  EResINT32,           ROffset(VSubdivisions) },
	{ "Orientation",    EResINT32,           ROffset(Orientation) },

	{ "UCenterAt",      EResINT8,            ROffset(UCenterAt) },
	{ "VCenterAt",      EResINT8,            ROffset(VCenterAt) },
	{ "Smooth",         EResBOOLEAN,         ROffset(Smooth) },
	{ "BackSide",       EResBOOLEAN,         ROffset(BackSide) },
	{ "Sides",          EResBOOLEAN,         ROffset(Sides) },
	{ "Triangles",      EResBOOLEAN,         ROffset(Triangles) },
};
#undef ROffset

static E3dObjectComponentClass*  _ComponentClass = NULL;

//========================================
// Set Vertex coordinates
//========================================
static void _SetVertices(E3dVertex* PVertices, const E3dVertexSize PVertexSize,  E3dGridInfo* PInfo)
{
	const int  LUSubdiv = PInfo->USubdivisions;
	const int  LVSubdiv = PInfo->VSubdivisions;

	const E3dCoordinate  LUEdgeLength = PInfo->UEdgeLength;
	const E3dCoordinate  LVEdgeLength = PInfo->VEdgeLength;

	const E3dCoordinate  LULenH = LUEdgeLength * 0.5;
	const E3dCoordinate  LVLenH = LVEdgeLength * 0.5;
	const E3dCoordinate  LThicknessH = PInfo->Thickness * 0.5;

	const E3dCoordinate  LUStep = LUEdgeLength / (E3dCoordinate)LUSubdiv;
	const E3dCoordinate  LVStep = LVEdgeLength / (E3dCoordinate)LVSubdiv;

	const E3dCoordinate  LUCurvature = PInfo->UCurvature;
	const E3dCoordinate  LVCurvature = PInfo->VCurvature;


	EIndex         LUCnt, LVCnt;
	E3dCoordinate  LX, LV;

	E3dVertex*  LVertex = PVertices;
	switch(PInfo->Orientation)
	{
		case E3dPO_XY:
			{
				E3dCoordinate  LVOffset;

				switch(PInfo->VCenterAt)
				{
					case E3dMIN:  LVOffset = LVLenH;break;
					default:      LVOffset = 0.0;break;
				}

// Front side
//
				for(LVCnt = 0, LV = LVLenH;LVCnt <= LVSubdiv;LVCnt++, LV -= LVStep)
				{
					for(LUCnt = 0, LX = -LULenH;LUCnt <= LUSubdiv;LUCnt++, LX += LUStep)
					{
						E3dVertex_Init(LVertex, PVertexSize,  LX, LV + LVOffset, LThicknessH + LUCurvature * LUEdgeLength * E3d_Cos(LX / LUEdgeLength * E3dPI) + LVCurvature * LVEdgeLength * E3d_Cos(LV / LVEdgeLength * E3dPI));
						EStruct_Inc(LVertex, PVertexSize);
					}
				}


				if(PInfo->BackSide)
				{
// Back side
//
					for(LVCnt = 0, LV = LVLenH;LVCnt <= LVSubdiv;LVCnt++, LV -= LVStep)
					{
						for(LUCnt = 0, LX = -LULenH;LUCnt <= LUSubdiv;LUCnt++, LX += LUStep)
						{
							E3dVertex_Init(LVertex, PVertexSize,  LX, LV + LVOffset, -LThicknessH + LUCurvature * LUEdgeLength * E3d_Cos(LX / LUEdgeLength * E3dPI) + LVCurvature * LVEdgeLength * E3d_Cos(LV / LVEdgeLength * E3dPI));
							EStruct_Inc(LVertex, PVertexSize);
						}
					}
				}
			}
		break;

		case E3dPO_XZ:
			{
				E3dCoordinate  LVOffset;

				switch(PInfo->VCenterAt)
				{
					case E3dMIN:     LVOffset = LVLenH;break;
					default:         LVOffset = 0.0;break;
				}

// Front side
//
				for(LVCnt = 0, LV = -LVLenH;LVCnt <= LVSubdiv;LVCnt++, LV += LVStep)
				{
					for(LUCnt = 0, LX = -LULenH;LUCnt <= LUSubdiv;LUCnt++, LX += LUStep)
					{
						E3dVertex_Init(LVertex, PVertexSize, LX, (LThicknessH + LUCurvature * LUEdgeLength * E3d_Cos(LX / LUEdgeLength * E3dPI) + LVCurvature * LVEdgeLength * E3d_Cos(LV / LVEdgeLength * E3dPI)), LV + LVOffset);
						EStruct_Inc(LVertex, PVertexSize);
					}
				}


				if(PInfo->BackSide)
				{
// Back side
//
					for(LVCnt = 0, LV = -LVLenH;LVCnt <= LVSubdiv;LVCnt++, LV += LVStep)
					{
						for(LUCnt = 0, LX = -LULenH;LUCnt <= LUSubdiv;LUCnt++, LX += LUStep)
						{
							E3dVertex_Init(LVertex, PVertexSize,  LX, (-LThicknessH + LUCurvature * LUEdgeLength * E3d_Cos(LX / LUEdgeLength * E3dPI) + LVCurvature*LVEdgeLength * E3d_Cos(LV / LVEdgeLength * E3dPI)), LV + LVOffset);
							EStruct_Inc(LVertex, PVertexSize);
						}
					}
				}
			}
		break;
	}
}


//================================================
// Copy editable fields
//================================================
static void _CopyFields(const E3dGridInfo* PSrc, E3dGridInfo* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(USubdivisions);
	_CopyField(VSubdivisions);
	_CopyField(Orientation);
	_CopyField(UCurvature);
	_CopyField(VCurvature);
	_CopyField(UEdgeLength);
	_CopyField(VEdgeLength);
	_CopyField(Thickness);
	_CopyField(Smooth);
	_CopyField(Triangles);
	_CopyField(BackSide);
	_CopyField(Sides);

	_CopyField(UCenterAt);
	_CopyField(VCenterAt);
#undef _CopyField
}


//========================================
// Update() (create/change) method
//========================================
static E3dObject* _Update1(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
//{ char  s[1024];printf("%s() %s:%d  %s%s%s  PCheckOnly %d  [%s]\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PObject ? PObject->Name : "-", ECodeNormal, PCheckOnly, E3dUpdateFlags_ToString(PUpdateFlags, s));fflush(stdout); }

	if(PCheckOnly) return(NULL);

//{ char  s[1024];printf("%s() %s:%d  %s%s%s  [%s]\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PObject ? PObject->Name : "-", ECodeNormal, E3dUpdateFlags_ToString(PUpdateFlags, s));fflush(stdout); }
//{ static int  c = 0;c++;assert(c < 1); }

	E3dGridInfo*   LInfo;
	E3dMesh*       LMesh;
	E3dPolyGroup*  LPolyGroup;

	if(PObject == NULL)
	{
		E_New(E3dMesh, LMesh);

		LInfo = (E3dGridInfo*)E3dObjectComponent_Add((E3dObject*)LMesh, _ComponentClass);
		if(PComponentTemplate) _CopyFields((E3dGridInfo*)PComponentTemplate, LInfo);
		LPolyGroup = E3dMesh_PolyGroupAdd(LMesh);if(LPolyGroup) E3dPolyGroup_VertexAttributeAdd(LPolyGroup,  NULL, E3dVtxAttribute_Normal, 0,  NULL);
		else
		{
			E_Delete(LMesh);return(NULL);
		}

		PObject = (E3dObject*)LMesh;
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);

		LMesh = (E3dMesh*)PObject;
		LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass((E3dObject*)LMesh, _ComponentClass, FALSE);
		if(!PCheckOnly && LInfo->AttributeChangeCount == 0 && LInfo->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail

		LPolyGroup = LMesh->PolyGroups.Items[0];
	}

	if(LMesh)
	{
//		const E3dCoordinate  LUEdgeLength = LInfo->UEdgeLength, LVEdgeLength = LInfo->VEdgeLength;
		int  LUSubdiv = LInfo->USubdivisions;
		int  LVSubdiv = LInfo->VSubdivisions;

		EIndex  LVNum = (LUSubdiv + 1) * (LVSubdiv + 1);
		if(LInfo->BackSide) LVNum *= 2;

		EIndex  LPNum = LUSubdiv * LVSubdiv;

		E3dMesh_FreeCaches(LMesh);
		E3dMesh_EdgesFree(LMesh);

// Allocate vertices for the Mesh
//
		const E3dVertexSize  LVertexSize = LMesh->VertexSize;
		if(LVNum != LMesh->Vertices.Count)
		{
			E3dMesh_VerticesFree(LMesh);
			E3dMesh_VerticesAdd(LMesh, LVNum);
		}

// Allocate Polygons for the Mesh
//
		if(LInfo->BackSide) LPNum *= 2;
		if(LInfo->Sides) LPNum += LUSubdiv * 2 + LVSubdiv * 2;
		if(LInfo->Triangles) LPNum *= 2;

		E3dPolygon*  LPolygons;
		if(LPolyGroup->Polygons.CountAllocated >= LPNum)
		{
			LPolygons = LPolyGroup->Polygons.Items;
			LPolyGroup->Polygons.Count = LPNum;
		}
		else
		{
			if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);
			LPolygons = E3dPolygons_New(LPNum);if(LPolygons)
			{
				LPolyGroup->Polygons.Items = LPolygons;LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LPNum;
			}
		}

// Set up the Mesh vertices
//
		_SetVertices(LMesh->Vertices.Items, LVertexSize,  LInfo);


// Build the Polygons
//
// Front side
//
		const E3dVertexNodeSize  LVertexNodeSize = LPolyGroup->VertexNodeSize;
//printf("%s() %s:%d LVertexNodeSize %d\n", __FUNCTION__, __FILE__, __LINE__, LVertexNodeSize);fflush(stdout);
		E3dPolygon*  LPolygon = E3d_GridBuildPolygons(LPolyGroup, LPolyGroup->Polygons.Items, LUSubdiv, LVSubdiv, 0, NULL, NULL, LInfo->Triangles, FALSE);

//		E3d_GridBuildPolygons(LPolyGroup->Polygons.Items, LVertexNodeSize, LUSubdiv, LVSubdiv, (LUSubdiv + 1) * (LVSubdiv + 1), LInfo->Triangles, FALSE);


// Rear side
//
		if(LInfo->BackSide)
		{
			EIndex  LRowA = (LUSubdiv + 1) * (LVSubdiv + 1), LNextRowA = LRowA + LUSubdiv + 1;

			if(LInfo->Triangles)
			{
				EIndex  LVCnt = 0;for(;LVCnt < LVSubdiv;LVCnt++, LRowA += LUSubdiv + 1, LNextRowA += LUSubdiv + 1)
				{
					EIndex  LUCnt = 0;for(;LUCnt < LUSubdiv;LUCnt++)
					{
						E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize, LNextRowA + LUCnt, LRowA + LUCnt, LRowA + LUCnt + 1);
						E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize, LRowA + LUCnt + 1, LNextRowA + LUCnt + 1, LNextRowA + LUCnt);
					}
				}
			}
			else
			{
				EIndex  LVCnt = 0;for(;LVCnt < LVSubdiv;LVCnt++, LRowA += LUSubdiv + 1, LNextRowA += LUSubdiv + 1)
				{
					EIndex  LUCnt = 0;for(;LUCnt < LUSubdiv;LUCnt++)
					{
						E3dPolygon_SetVa(LPolygon++, LVertexNodeSize, 4, LRowA + LUCnt + 1, LNextRowA + LUCnt + 1, LNextRowA + LUCnt, LRowA + LUCnt);
					}
				}
			}

		}


		if(LInfo->Smooth)
		{
			LPolyGroup->VertexNormalType = E3dNormalFROM_SMOOTHING_ANGLE;
			LPolyGroup->Flags |= E3dUSE_VERTEX_NORMALS;
		}
		else
		{
			LPolyGroup->VertexNormalType = E3dNONE;
			LPolyGroup->Flags &= ~E3dUSE_VERTEX_NORMALS;
		}

		if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;
		LInfo->AttributeChangeCount = LInfo->InputChangeCount = 0;
	}

	return(PObject);
}


//========================================
// Dialog-box callback handlers
//========================================
static void _DCB_Horizontal(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dPolyGroup*    LPolyGroup = LMesh->PolyGroups.Items[0];
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);


	if(((XeToggleButtonCallbackStruct*)PCallData)->Set) LInfo->Orientation = E3dPO_XZ;
	else LInfo->Orientation = E3dPO_XY;

	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	E3dMesh_RecomputePolygonNormals(LMesh);
	E3dMeshPolyGroup_RefreshNormals(LMesh, LPolyGroup, LPolyGroup->VertexNormalType, LPolyGroup->SmoothingAngle);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE, 0,  LScene);

	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_Smooth(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dPolyGroup*    LPolyGroup = LMesh->PolyGroups.Items[0];
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);

	LInfo->Smooth = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
	if(LInfo->Smooth)
	{
		LPolyGroup->VertexNormalType = E3dNormalFROM_SMOOTHING_ANGLE;
		E3dMesh_RecomputePolygonNormals(LMesh);
		E3dMeshPolyGroup_RefreshNormals(LMesh, LPolyGroup, LPolyGroup->VertexNormalType, LPolyGroup->SmoothingAngle);
		LPolyGroup->Flags |= E3dUSE_VERTEX_NORMALS;
	}
	else
	{
		LPolyGroup->VertexNormalType = E3dNONE;
		LPolyGroup->Flags &= ~E3dUSE_VERTEX_NORMALS;
	}

	E3dObject_UpdateForDisplay(LObject,  E3dUpdateVERTEX_NORMAL, 0,  NULL);

	E3dWindows_RedrawByChangeFlags(E3d_Scene, E3dChgVERTEX_NORMAL);
}


static void _DCB_Triangles(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->Triangles = ((XeToggleButtonCallbackStruct*)PCallData)->Set;LInfo->AttributeChangeCount++;

	E3dObject_CustomUpdate(LObject, LInfo,  E3dCallALL,  &LChanged);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _DCB_Back(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->BackSide = ((XeToggleButtonCallbackStruct*)PCallData)->Set;LInfo->AttributeChangeCount++;

	E3dObject_CustomUpdate(LObject, LInfo,  E3dCallALL,  &LChanged);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _DCB_CenterAtMin(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	if(((XeToggleButtonCallbackStruct*)PCallData)->Set)
	{
		LInfo->UCenterAt = E3dCENTER;
		LInfo->VCenterAt = E3dMIN;
	}
	else
	{
		LInfo->UCenterAt = E3dCENTER;
		LInfo->VCenterAt = E3dCENTER;
	}

	LInfo->AttributeChangeCount++;
	E3dObject_CustomUpdate(LObject, LInfo,  E3dCallALL,  &LChanged);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _DCB_UEdgeLength(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->UEdgeLength = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_VEdgeLength(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->VEdgeLength = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_Thickness(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->Thickness = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_USubdivisions(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->USubdivisions = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	E3dObject_CustomUpdate(LObject, LInfo,  E3dCallALL,  &LChanged);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _DCB_VSubdivisions(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->VSubdivisions = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	E3dObject_CustomUpdate(LObject, LInfo,  E3dCallALL,  &LChanged);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _DCB_UCurvature(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->UCurvature = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_VCurvature(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dMesh*         LMesh = (E3dMesh*)LObject;
	E3dGridInfo*     LInfo = (E3dGridInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);
	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LInfo->VCurvature = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
	_SetVertices(LMesh->Vertices.Items, LMesh->VertexSize,  LInfo);
	LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


//========================================
// Pop up the dialog box
//========================================
static void _GUI(E3dObject* PObject, E3dObjectComponent* PInfo, void* PPanel, void* PDrawer, const int PAction)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dResourcesGUI*         LResourcesGUI;
	E3dGridInfo*             LInfo = (E3dGridInfo*)PInfo;

	switch(PAction)
	{
		case E_UPDATE:
			if(LInfo)
			{
				if(LDrawer->ResourcesGUI)
				{
					LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

					XeToggleButton_SetState(LResourcesGUI->HorizontalBW, (EBool)(LInfo->Orientation == E3dPO_XZ), FALSE);
					XeToggleButton_SetState(LResourcesGUI->SmoothBW, LInfo->Smooth, FALSE);
					XeToggleButton_SetState(LResourcesGUI->TrianglesBW, LInfo->Triangles, FALSE);
					XeToggleButton_SetState(LResourcesGUI->BackSideBW, LInfo->BackSide, FALSE);
					XeToggleButton_SetState(LResourcesGUI->CenterAtMinBW, LInfo->UCenterAt == E3dCENTER && LInfo->VCenterAt == E3dMIN, FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->UEdgeLengthScaleW, LInfo->UEdgeLength, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->VEdgeLengthScaleW, LInfo->VEdgeLength, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->ThicknessScaleW, LInfo->Thickness, FALSE);
					XeScale_SetValue(LResourcesGUI->USubdivisionsScaleW, LInfo->USubdivisions, FALSE);
					XeScale_SetValue(LResourcesGUI->VSubdivisionsScaleW, LInfo->VSubdivisions, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->UCurvatureScaleW, LInfo->UCurvature, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->VCurvatureScaleW, LInfo->VCurvature, FALSE);
				}
				else
				{
					XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LMatrixW, LW;
					XeWidget  LIndentWs[7];int  LIndentN;
					XeWidget  LTraverseWs[8];int  LTraverseN;
					EArg      Args[256];int  ArgCnt;

					ENew(LResourcesGUI);LDrawer->ResourcesGUI = LResourcesGUI;

					const XeCoordinate  LSpacingLeft = XeDrawer_LeftIndent, LSpacingRight = 4;

					EXeStart;
					EXeSetArg(XeNSpacingBottom, 4);
					Xe_SetValues(LTopMatrixW,  Args, ArgCnt);

					{
						EXeStart;
						EXeSetArg(XeNSpacingLeft, LSpacingLeft);
						EXeSetArg(XeNOrientation, XeHORIZONTAL);
						EXeSetArg(XeNSpacingRight, 8);
						EXeSetArg(XeNSpacingX, 8);
						EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						LMatrixW = XeWidget_ManagedNew("MatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LInfo->Orientation == E3dPO_XZ);
						LResourcesGUI->HorizontalBW = LW = XeWidget_ManagedNew("Horizontal", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB_Horizontal, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LInfo->Smooth);
						LResourcesGUI->SmoothBW = LW = XeWidget_ManagedNew("Smooth", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB_Smooth, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNFontList, Xe_LabelFontList);
						LResourcesGUI->TrianglesBW = LW = XeWidget_ManagedNew("Triangles", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB_Triangles, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LInfo->BackSide);
						LResourcesGUI->BackSideBW = LW = XeWidget_ManagedNew("Back", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB_Back, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LInfo->UCenterAt == E3dCENTER && LInfo->VCenterAt == E3dMIN);
						LResourcesGUI->CenterAtMinBW = LW = XeWidget_ManagedNew("Center at min.", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB_CenterAtMin, (EPointer)LPanel);
					}

/*
					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNSet, LInfo->Sides);
					LResourcesGUI->SidesBW = LW = XeWidget_ManagedNew("Sides", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_Sides, (EPointer)LPanel);
*/

					EXeStart;
					EXeSetArg(XeNSpacingTop, 2);EXeSetArg(XeNSpacingBottom, 2);
					EXeSetArg(XeNShadowThickness, 2);
					EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);


					LTraverseN = 0;LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 7);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->UEdgeLengthScaleW = LW = XeScale_New_Coord(LTopMatrixW, "U Edge length",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->UEdgeLength,  0.0001, 1000000000.0,  0.0001, 1000.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_UEdgeLength, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 7);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->VEdgeLengthScaleW = LW = XeScale_New_Coord(LTopMatrixW, "V Edge length",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->VEdgeLength,  0.0001, 1000000000.0,  0.0001, 1000.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_VEdgeLength, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 7);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->ThicknessScaleW = LW = XeScale_New_Coord(LTopMatrixW, "Thickness",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->Thickness,  0.0, 1000000000.0,  0.0, 1000.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_Thickness, (EPointer)LPanel);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 0);


					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 5);
					EXeSetArg(XeNMinimum, 1);EXeSetArg(XeNMaximum, 10000);EXeSetArg(XeNValue, LInfo->USubdivisions);
					EXeSetArg(XeNScaleMinimum, 1);EXeSetArg(XeNScaleMaximum, 100);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->USubdivisionsScaleW = LW = XeWidget_ManagedNew("U Subdivisions", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_USubdivisions, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 5);
					EXeSetArg(XeNMinimum, 1);EXeSetArg(XeNMaximum, 10000);EXeSetArg(XeNValue, LInfo->VSubdivisions);
					EXeSetArg(XeNScaleMinimum, 1);EXeSetArg(XeNScaleMaximum, 100);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->VSubdivisionsScaleW = LW = XeWidget_ManagedNew("V Subdivisions", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_VSubdivisions, (EPointer)LPanel);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);


					EXeStart;
					EXeSetArg(XeNSpacingTop, 4);EXeSetArg(XeNSpacingBottom, 4);
					EXeSetArg(XeNShadowThickness, 2);
					EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 7);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->UCurvatureScaleW = LW = XeWidget_ManagedNew("U Curvature", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeScale_SetValueAndRange_Coord(LW,  LInfo->UCurvature,  -1.0, 1.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_UCurvature, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNScaleLengthInPixels, 200);
					EXeSetArg(XeNTextFieldDigits, 7);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->VCurvatureScaleW = LW = XeWidget_ManagedNew("V Curvature", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeScale_SetValueAndRange_Coord(LW,  LInfo->VCurvature,  -1.0, 1.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_VCurvature, (EPointer)LPanel);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, LSpacingLeft);


// Set up TextField traversal
//
					XeTraversal_SetRing(LTraverseWs, LTraverseN);
				}
			}
		break;
	}
}


//================================================
// Menu callback
//================================================
static void _MCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_ComponentClass);
}


//================================================
//================================================
static int _Init(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dGridInfo*  LThis = (E3dGridInfo*)PInfo;

	LThis->USubdivisions = 1;LThis->VSubdivisions = 1;
	LThis->Orientation = E3dPO_XZ;
	LThis->UCurvature = 0.0;LThis->VCurvature = 0.0;
	LThis->UEdgeLength = 12.0;
	LThis->VEdgeLength = 12.0;
	LThis->Thickness = 0.0;

	LThis->Smooth = TRUE;
	LThis->Triangles = FALSE;
	LThis->BackSide = FALSE;
	LThis->Sides = FALSE;

	LThis->UCenterAt = E3dCENTER;
	LThis->VCenterAt = E3dCENTER;

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _Copy(const E3dObject* PSObject, const E3dObjectComponent* PSrcInfo,  E3dObject* PDObject, E3dObjectComponent* PDstInfo)
{
	const E3dGridInfo*  LSrcInfo = (E3dGridInfo*)PSrcInfo;
	E3dGridInfo*        LDstInfo = (E3dGridInfo*)PDstInfo;

	_CopyFields(LSrcInfo, LDstInfo);
}


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

	{
		E3dObjectComponentClass  LObjectComponentClass=
		{
			.Name=        "Grid",
			.StructSize=  sizeof(E3dGridInfo),
			.Resources=   EListS_s(_Resources),
			.Init=        _Init,
			.Copy=        _Copy,
			.Update1=      _Update1,
			.GUI=         _GUI
		};

//if(LObjectComponentClass.Free) { printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout); }
		_ComponentClass = E3dObjectComponent_ClassRegister(&LObjectComponentClass);

//printf("%s() %s:%d  _ComponentClass %s%p%s %p %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, _ComponentClass, ECodeNormal, _ComponentClass->Free, (int)EOffset(E3dObjectComponentClass*, Free));fflush(stdout);

	}

	if(_ComponentClass)
	{
		EpM_CreateIconStates3_sp(Grid);E_Assign(_Image_Grid, _ComponentClass->Icon);
		XePushButton_AddImg3(_ToolBarButton, Grid, "Tool->Create", "Grid", '\0', NULL, NULL, TRUE, "Create plane or grid.\nRight button:  dialog box for this tool", _MCB, (EPointer)0);
		_MenuButton = XePushButton_Add(Xe_App, "Menu->Create", "Grid", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
	}

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		XeItemFree(_MenuButton);
		XeItemFree(_ToolBarButton);
	}

	if(_ComponentClass) E3dObjectComponent_ClassDeactivate(_ComponentClass);

	EpIconFree3(Grid);

	return(E_SUCCESS);
}
