/*======================================================================================================*/
/* Hierarchies												*/
/*													*/
/* Plugin for EQUINOX-3D										*/
/*													*/
/* - Collapse branches											*/
/* - Connect/disconnect Model node									*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Oct-22 23:20:54									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <stdio.h>

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

#include <E3D/Matrix.h>
#include <E3D/Model.h>
#include <E3D/Panel.h>
#include <E3D/Pick.h>
#include <E3D/Polygon.h>
#include <E3D/Scene.h>
#include <E3D/Select.h>
#include <E3D/StatusPanel.h>	// For E3dp_PrintMessage

#include <EGUI/PushButton.h>
#include <EGUI/Dialog.h>

#include <Xe/ToggleButton.h>

extern EpCImage		Image_Connect;
extern EpCImage		Image_ConnectActive;
extern EpCImage		Image_ConnectArm;
extern EpCImage		Image_Disconnect;
extern EpCImage		Image_DisconnectActive;
extern EpCImage		Image_DisconnectArm;


static EPlugin*		_Plugin=NULL;

static EguiItem		E3d_Dialog=NULL;

static EguiItem		_MenuButtons[16], _ToolPanelButtons[16];

static unsigned int	_NumOfMenuButtons=0, _NumOfToolPanelButtons=0;


static EBool		E3d_TransformCompensation=TRUE,
			_ConnectMode=FALSE;


static Arg		Args[256];
static Cardinal		ArgCnt;


enum
{
 E3dCR_CONNECT=0,
 E3dCR_DISCONNECT
};

enum
{
 E3dCR_TOGGLE_HRC_COMPENSATION=EguiCUSTOM0,
};


/*==============================================================*/
/* Connect/disconnect pick callback				*/
/*==============================================================*/
static void _PickCB_ConnectDisconnect(E3dPickCallbackStruct* LPickStruct, int LNumOfItems, E3dItem* LPickedItems)
{
 E3dModel**	LModels;
 unsigned int	LC, LNumOfModels;
 EBool		LChanged=FALSE;

 if(LPickStruct->Reason==E3dCR_ABORT)
 {
  _ConnectMode=FALSE;
  return;
 }

 if(LPickStruct->Button==Button3)
 {
  E3dp_PickEnd();
  _ConnectMode=FALSE;
  return;
 }

 LModels=NULL;
 switch(LPickStruct->Button)
 {
  case Button1:		// Pick child
   if(LNumOfItems>0)
   {
    if((LNumOfModels=E3d_SceneGetSelectedModels(E3d_Scene, &LModels, TRUE))>0)
    {
     if(LNumOfModels<2)
     {
      E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;
      E3dModel*		LParentModel;
      E3dModel*		LChildModel;

      LChildModel=LGeoItem->Model;

      LParentModel=LModels[0];

//      E3dp_PrintMessage(0, 0, "Picked child: [%s]", LChildModel->Name);

      if(LChildModel!=LParentModel)
      {
       if(LChildModel->Parent==NULL)
       {
	E3d_ModelConnectChild(LParentModel, LChildModel);

	E3d_SelectModelBranch(LParentModel, FALSE);

	E3d_ModelHrcRefreshMaterialInheritance(LParentModel);
	LChanged=TRUE;
       }
      }
      else E3dp_PrintMessage(0, 0, "Pick a different Model for child");
     }
     else E3dp_PrintMessage(0, 0, "Select only one Model for parent");

     EFree(LModels);
    }
    else E3dp_PrintMessage(0, 0, "No Models selected");
   }
  break;

  case Button2:		// Pick parent
   if(LNumOfItems>0)
   {
    E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;
    E3dModel*	LParentModel=LGeoItem->Model;

    if((LNumOfModels=E3d_SceneGetSelectedModels(E3d_Scene, &LModels, TRUE))>0)
    {
     E3dModel*	LModel;

     E3dp_PrintMessage(0, 0, "Picked parent: [%s]", LParentModel->Name);

     for(LC=0;LC<LNumOfModels;LC++)
     {
      LModel=LModels[LC];

      if(LModel!=LParentModel)
      {
       if(LModel->Parent==NULL)
       {
	E3d_ModelConnectChild(LParentModel, LModel);

	LChanged=TRUE;
       }
      }
     }

     EFree(LModels);

     if(LChanged)
     {
      E3d_SelectModelBranch(LParentModel, TRUE);
      E3d_ModelHrcRefreshMaterialInheritance(LParentModel);
     }
    }
    else
    {
     E3dGeometry**	LSelectedGeometries;
     int		LNumOfSelectedGeometries;

     if((LNumOfSelectedGeometries=E3d_SceneGetSelectedGeometries(E3d_Scene, &LSelectedGeometries, E3dGEO_ANY))>0)
     {
      E3dGeometry*	LGeometry;

      E3dp_PrintMessage(0, 0, "Picked parent: [%s]", LParentModel->Name);

      for(LC=0;LC<LNumOfSelectedGeometries;LC++)
      {
       LGeometry=LSelectedGeometries[LC];

       if(E3d_ModelAppendGeometry(LParentModel, LGeometry))
       {
	LChanged=TRUE;
       }
      }

      EFree(LSelectedGeometries);

      if(LChanged)
      {
       E3d_SelectModelBranch(LParentModel, TRUE);
       E3d_ModelHrcRefreshMaterialInheritance(LParentModel);
      }
     }
     else E3dp_PrintMessage(0, 0, "Select Models or Geometries to be 'parented'");
    }
   }
// This will call us with an E3dCR_ABORT message
//
   E3dp_ResetWorkModes();
  break;
 }

 if(LChanged)
 {
  E3dp_SceneLayoutOnSchematics(E3d_Scene);
  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
 }
}


/*======================================*/
/* Dialog box callback			*/
/*======================================*/
static void E3dDCB(EguiItem LI, EPointer LClientData, EPointer LCallData)
{
 switch((int)LClientData)
 {
  case E3dCR_TOGGLE_HRC_COMPENSATION:
   if(((XeToggleButtonCallbackStruct*)LCallData)->set) E3d_TransformCompensation=TRUE;
   else E3d_TransformCompensation=FALSE;
  break;

  case EguiCANCEL:
   EGUI_UndisplayShell(E3d_Dialog);
   _Plugin->LockCount-=1;
  break;
 }
}


/*======================================================*/
/* Popup connect/disconnect settings dialog box		*/
/*======================================================*/
void E3d_PopupDialog()
{
 Widget			LFormW, LTAW;
 EguiArg		LArgs[32];
 int			LArgCnt;
 EguiItem		LDialog, LI;
 EguiDialogRec*		LD;

 if(E3d_Dialog==NULL)
 {
  LArgCnt=0;
  EguiSetArg(EguiNallowShellResize, True, LArgs, LArgCnt);
  EguiSetArg(EguiNstretchButtons, False, LArgs, LArgCnt);
  EguiSetArg(EguiNworkAreaType, EguiFORM, LArgs, LArgCnt);
  EguiSetArg(EguiNwmDecorations, EguiWM_DECOR_BORDER|EguiWM_DECOR_RESIZEH|EguiWM_DECOR_TITLE, LArgs, LArgCnt);
  EguiSetArg(EguiNtitle, "Hierarchy connect/disconnect settings", LArgs, LArgCnt);EguiSetArg(EguiNiconName, "Hierarchy", LArgs, LArgCnt);
  if((LDialog=EGUI_CreateDialog(EguiDIALOG_TEMPLATE, "Hierarchy", E3dp_TopShell, LArgs, LArgCnt))==NULL) return;

  E3d_Dialog=LDialog;
  LArgCnt=0;
  EguiSetArg(EguiNshowAsDefault, True, LArgs, LArgCnt);
  LI=EGUI_CreatePushButton("Close", LDialog, LArgs, LArgCnt);
  EGUI_AddCallback(LI, EguiNactivateCallback, E3dDCB, (EPointer)EguiCANCEL);

  LD=(EguiDialogRec*)LDialog;

// This part is not using EGUI, the platform-independent GUI API yet, that will change!
//
  LFormW=EGUI_DialogGetChild(LDialog, EguiDIALOG_WORK_AREA);

  EXtStart;
  EXtSetArg(XeNtopAttachment, XeATTACH_FORM);EXtSetArg(XeNtopOffset, 2);
  EXtSetArg(XeNbottomAttachment, XeATTACH_FORM);;EXtSetArg(XeNbottomOffset, 5);
  EXtSetArg(XeNleftAttachment, XeATTACH_FORM);EXtSetArg(XeNleftOffset, 5);
  EXtSetArg(XeNshadowThickness, 0);
  EXtSetArg(XeNhighlightThickness, 0);
  EXtSetArg(XeNmarginLeft, 0);EXtSetArg(XeNmarginRight, 0);EXtSetArg(XeNmarginTop, 0);
  EXtSetArg(XeNmarginBottom, 0);
  EXtSetArg(XeNrecomputeSize, False);
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNforeground, EGUI_ForegroundColor);
  EXtSetArg(XeNtopShadowColor, EGUI_TopShadowColor);
  EXtSetArg(XeNbottomShadowColor, EGUI_BottomShadowColor);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNselectColor, EGUI_HighlightColor);
  EXtSetArg(XeNset, E3d_TransformCompensation);
  LTAW=XtCreateManagedWidget("Transform compensation", xeToggleButtonWidgetClass, LFormW, Args, ArgCnt);
  XtAddCallback(LTAW, XeNvalueChangedCallback, (XtCallbackProc)E3dDCB, (XtPointer)E3dCR_TOGGLE_HRC_COMPENSATION);


  EGUI_RaiseShell(E3d_Dialog);
  EGUI_CenterShellToItem(E3d_Dialog, E3dp_TopShell);
  _Plugin->LockCount+=1;
 }
 else
 {
  if(EGUI_ShellDisplayStatus(E3d_Dialog)!=EguiDISPLAYED)
  {
   _Plugin->LockCount+=1;
  }

  EGUI_RaiseShell(E3d_Dialog);
 }

 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
}


/*==============================================================*/
/* Connect/disconnect						*/
/*==============================================================*/
void E3dCB_ConnectDisconnect(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dp_ResetWorkModes();
 if(((EguiPushButtonCallbackStruct*)LCallData)->Reason==ECR_OPTIONS) { E3d_PopupDialog();return; }

 switch((int)LClientData)
 {
  case E3dCR_CONNECT:
   if(!_ConnectMode)
   {
    E3dModel**		LModels;
    unsigned int	LNumOfModels;

    if((LNumOfModels=E3d_SceneGetSelectedModels(E3d_Scene, &LModels, TRUE))>0)
    {
     if(E3dp_PickRequest(E3dITEM_MODEL, _PickCB_ConnectDisconnect, 0, FALSE)==0)
     {
      E3dp_PushMessage(0, 0, "Connect  LMB: pick child   MMB: pick parent   RMB: cancel");
      _ConnectMode=TRUE;
     }
     EFree(LModels);
    }
    else
    {
     E3dGeometry**	LSelectedGeometries;
     int		LNumOfSelectedGeometries;

     if((LNumOfSelectedGeometries=E3d_SceneGetSelectedGeometries(E3d_Scene, &LSelectedGeometries, E3dGEO_ANY))>0)
     {
      EFree(LSelectedGeometries);

      if(E3dp_PickRequest(E3dITEM_MODEL, _PickCB_ConnectDisconnect, 0, FALSE)==0)
      {
       if(LNumOfSelectedGeometries>1) E3dp_PushMessage(0, 0, "Connect  MMB: pick Model for selected Geometries  RMB: cancel");
       else E3dp_PushMessage(0, 0, "Connect  MMB: pick Model for selected Geometry  RMB: cancel");
       _ConnectMode=TRUE;
      }

     }
     else E3dp_PrintMessage(0, 0, "Select Models or Geometries to connect");
    }
   }
  break;

  case E3dCR_DISCONNECT:
   {
    unsigned int	LC, LN, LNDisconnectedInHrc, LNDisconnectedTotal,
			LNumOfNewRootModels, LNumOfNewRootModelsAllocated;
    E3dModel**		LRootModels;
    E3dModel**		LNewRootModels;
    E3dModel*		LRootModel;
    E3dModel*		LModel;

    LRootModels=E3d_Scene->RootModels;
    LNDisconnectedTotal=0;

    LNumOfNewRootModels=0;LNumOfNewRootModelsAllocated=0;
    LNewRootModels=NULL;

    for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
    {

     LModel=LRootModel=LRootModels[LC];
     LNDisconnectedInHrc=0;
     do
     {
      switch(LModel->Selection)
      {
       case E3dSEL_NODE:
       case E3dSEL_BRANCH_ROOT:
	if(LModel->Parent!=NULL)
	{
	 E3dModel*	LTModel=LModel;

//printf("LModel->Name %s Next: %08x\n", LTModel->Name, LModel->Next);fflush(stdout);
	 LModel=E3d_ModelHrcBranchDisconnectFromParent(LTModel, TRUE);

//printf("LModel->Name %s Next: %08x\n\n", LTModel->Name, LModel);fflush(stdout);
	 ELst_AddPointerA((void***)(&LNewRootModels), &LNumOfNewRootModels, &LNumOfNewRootModelsAllocated, 4, LTModel);

	 E3d_ModelHrcRefreshHierarchy(LTModel);
	 E3d_ModelHrcRefreshMatrices(LTModel);

         E3d_ModelHrcUpdateForDisplay(LTModel, E3dGF_SHAPE);

	 LNDisconnectedInHrc++;
	}
	else LModel=LModel->Next;
       break;

       default:	LModel=LModel->Next;break;
      }
     } while(LModel!=NULL);

     LNDisconnectedTotal+=LNDisconnectedInHrc;

     if(LNDisconnectedInHrc>0)
     {
      E3d_ModelHrcRefreshHierarchy(LRootModel);
      E3d_ModelHrcRefreshMatrices(LRootModel);
      E3d_ModelHrcUpdateForDisplay(LRootModel, E3dGF_ALL-E3dGF_REMOVE_STRIPS);
     }
    }

    if(LNDisconnectedTotal>0)
    {
     if(LNumOfNewRootModels>0)
     {
      for(LC=0;LC<LNumOfNewRootModels;LC++) E3d_SceneAddModelHrc(E3d_Scene, LNewRootModels[LC]);

      EFree(LNewRootModels);
     }

     E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
     E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
    }
   }
  break;
 }
}


/*==============================================================*/
/* Collapse selected branches into one Model node per branch	*/
/*==============================================================*/
void E3dpCB_CollapseBranch(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dModel**	LRootModels;
 E3dModel*	LRootModel;
 E3dModel*	LBranchRootModel;
 E3dModel*	LModelNextAfterBranch;
 E3dModel*	LModel;
 E3dModel*	LModelT;
 E3dModel	LTmpModel;
 E3dGeometry*	LGeometry;
 E3dMatrix	LMatrix, LNormalMatrix;
 unsigned int	LC, LN, LBranchesCollapsed;
 unsigned int	LGmCnt, LGmN;

 LBranchesCollapsed=0;
 LRootModels=E3d_Scene->RootModels;
 for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
 {
  LModel=LRootModel=LRootModels[LC];

  do
  {
// Take care of this branch in one sitting
//
   if((LModel->Selection==E3dSEL_BRANCH_ROOT)&&(LModel->Child!=NULL))
   {
    LBranchRootModel=LModel;

    LModelNextAfterBranch=E3d_ModelHrcBranchGetNodeAfter(LBranchRootModel);

if(LModelNextAfterBranch)
{
printf("LModelNextAfterBranch %s\n", LModelNextAfterBranch->Name);
}
else printf("LModelNextAfterBranch NULL\n");
fflush(stdout);

    memcpy(&LTmpModel, LBranchRootModel, sizeof(E3dModel));	// Back-up transforms and Parent/NextSibling of branch-root

// Disconnect branch from the hierarchy
//
    E3d_ModelResetTransforms(LBranchRootModel);
    LBranchRootModel->Parent=NULL;LBranchRootModel->NextSibling=NULL;
    E3d_ModelHrcRefreshHierarchy(LBranchRootModel);
    E3d_ModelHrcRefreshMatrices(LBranchRootModel);

    LModel=LModel->Child;


    do
    {
     LGmN=LModel->NumOfGeometries;
     if(LGmN)
     {
      E3dGeometry**	LGeometries=(E3dGeometry**)EMalloc(sizeof(E3dGeometry*)*LGmN);

      memcpy(LGeometries, LModel->Geometries, sizeof(E3dGeometry*)*LGmN);

      E3d_MatrixCopy(LModel->LocalToWorldMatrix, LMatrix);
      E3d_MatrixCopy(LModel->NormalLocalToWorldMatrix, LNormalMatrix);

      for(LGmCnt=0;LGmCnt<LGmN;LGmCnt++)
      {
       LGeometry=LGeometries[LGmCnt];
       if(LGeometry)
       {
	E3d_GeometryTransform(LGeometry, LMatrix, LNormalMatrix);

	E3d_ModelAppendGeometry(LBranchRootModel, LGeometry);
	E3d_ModelRemoveGeometry(LModel, LGeometry);
	if(LGeometry->Name==NULL) LGeometry->Name=EStrDup(LModel->Name);
       }
      }
      EFree(LGeometries);
     }

     LModelT=LModel;
     LModel=LModel->Next;

     E3d_ModelFree(LModelT);

     if(LModel==NULL) break;
    } while(LModel!=LModelNextAfterBranch);


    LBranchesCollapsed++;

    LTmpModel.Geometries=LBranchRootModel->Geometries;
    LTmpModel.NumOfGeometries=LBranchRootModel->NumOfGeometries;
    memcpy(LBranchRootModel, &LTmpModel, sizeof(E3dModel));	// Restore transforms and Parent/NextSibling of branch-root

    LBranchRootModel->Child=NULL;LBranchRootModel->Next=LModelNextAfterBranch;
    if(LModelNextAfterBranch!=NULL) LModelNextAfterBranch->Prev=LBranchRootModel;
    E3d_ModelUpdateForDisplay(LBranchRootModel, E3dDF_ALL);
   }
   else LModel=LModel->Next;
  } while(LModel);

  E3d_ModelHrcRefreshHierarchy(LRootModel);
  E3d_ModelHrcRefreshMatrices(LRootModel);
  E3d_ModelHrcRefreshMaterialInheritance(LRootModel);
 }

 if(LBranchesCollapsed==0) E3dp_PrintMessage(0, 5000, "No branches selected\n");
 else
 {
  if(LBranchesCollapsed==1) E3dp_PrintMessage(0, 5000, "Collapsed 1 branch");
  else E3dp_PrintMessage(0, 5000, "Collapsed %d branches", LBranchesCollapsed);
  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
 }

}


/*======================================*/
/* Entry point of the plugin		*/
/*======================================*/
int Plugin_Init(EPlugin* LPlugin)
{
 EpImage*	LImage;
 EpImage*	LActiveImage;
 EpImage*	LArmImage;


 _Plugin=LPlugin;

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Hierarchy", "Collapse branch", '\0', NULL, NULL, FALSE, NULL, E3dpCB_CollapseBranch, (EPointer)0);

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Hierarchy", "Connect node or branch", '\0', NULL, NULL, TRUE, NULL, E3dCB_ConnectDisconnect, (EPointer)E3dCR_CONNECT);
 EpM_RGBA8ImageFromCStruct(LImage, Image_Connect);
 EpM_RGBA8ImageFromCStruct(LActiveImage, Image_ConnectActive);
 EpM_RGBA8ImageFromCStruct(LArmImage, Image_ConnectArm);
 _ToolPanelButtons[_NumOfToolPanelButtons++]=EGUI_AddPushButtonImg("Tool->Hierarchy", "Connect", '\0', NULL, NULL, FALSE, "Connect selected Model(s) to a hierarchy or selected Geometries to a Model\nRight mouse button: settings for this tool", E3dCB_ConnectDisconnect, (EPointer)E3dCR_CONNECT, LImage, LActiveImage, LArmImage);

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Hierarchy", "Disconnect node or branch", '\0', NULL, NULL, TRUE, NULL, E3dCB_ConnectDisconnect, (EPointer)E3dCR_DISCONNECT);
 EpM_RGBA8ImageFromCStruct(LImage, Image_Disconnect);
 EpM_RGBA8ImageFromCStruct(LActiveImage, Image_DisconnectActive);
 EpM_RGBA8ImageFromCStruct(LArmImage, Image_DisconnectArm);
 _ToolPanelButtons[_NumOfToolPanelButtons++]=EGUI_AddPushButtonImg("Tool->Hierarchy", "Disconnect", '\0', NULL, NULL, FALSE, "Disconnect selected Model(s) from their parent(s)\nRight mouse button: settings for this tool", E3dCB_ConnectDisconnect, (EPointer)E3dCR_DISCONNECT, LImage, LActiveImage, LArmImage);

 return(0);
}


/*======================================*/
/* Exit method of the plugin		*/
/*======================================*/
int Plugin_Exit()
{
 unsigned int	LC;

 for(LC=0;LC<_NumOfMenuButtons;LC++) if(_MenuButtons[LC]) EGUI_DestroyItem(_MenuButtons[LC]);
 for(LC=0;LC<_NumOfToolPanelButtons;LC++) if(_ToolPanelButtons[LC]) EGUI_DestroyItem(_ToolPanelButtons[LC]);

 return(0);
}
