/* ui_galerkin.c: Galerkin Radiosity User Interface */

#include <Xm/MessageB.h>

#include "galerkin.h"
#include "galerkinP.h"
#include "uit.h"
#include "scene.h"
#include "statistics.h"
#include "canvas.h"
#include "render.h"
#include "vertex.h"
#include "error.h"
#include "scratch.h"

static Widget galerkinControlPanel;

/* The number of needed coefficients for radiance, received radiance and unshot 
 * radiance may change after choosing another approximation order or iteration 
 * method (unshot radiance is only needed for shooting e.g.). ReallocCoefficients()
 * traverses the element hierarchies of all patches and takes care of reallocating
 * space for the coefficients. */
static void ElementReallocCoefficientsRecursive(ELEMENT *elem)
{
  ITERATE_REGULAR_SUBELEMENTS(elem, ElementReallocCoefficientsRecursive);
  ElementReallocCoefficients(elem);
}

static void PatchReallocCoefficients(PATCH *patch)
{
  ElementReallocCoefficientsRecursive(TOPLEVEL_ELEMENT(patch));
}

static void ReallocCoefficients(void)
{
  CanvasPushMode(CANVASMODE_WORKING);
  PatchListIterate(Patches, PatchReallocCoefficients);
  CanvasPullMode();
}

/* Interactions are kept with the source element for shooting and with the
 * receiver for gathering. When switching from shooting to gathering, the
 * interactions must be moved from receiver to source and vice versa. */
static void MoveInteractions(void)
{
  Error("MoveInteractions", "Not yet implemented");
}

static void IterationMethodCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
  ITERATION_METHOD new_iteration_method = (ITERATION_METHOD)client_data;

  if (set == XmSET) {
    if (gal.iteration_method != new_iteration_method) {
      if (gal.iteration_method == SOUTHWELL) {
	/* Interactions are kept with the source element for shooting and with the
	 * receiver for gathering. When switching from shooting to gathering, the
	 * interactions must be moved from receiver to source and vice versa. */
	MoveInteractions();
      } else if (new_iteration_method == SOUTHWELL) {
	/* Switching from gathering to shooting is a bad idea. Shooting only converges
	 * when starting with the selfemitted radiance as initial unshot and
	 * total radiance. */
	Warning(NULL, "Reinitialize the radiance computations");
      }
      gal.iteration_method = new_iteration_method;
      ReallocCoefficients();
    }
  }
}

static void ShaftCullModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
  if (set == XmSET) 
    gal.shaftcullmode = (SHAFTCULLMODE)client_data;
}

static void ClusteringStrategyCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int set = ((XmToggleButtonCallbackStruct *)call_data)->set;
  if (set == XmSET) 
    gal.clustering_strategy = (CLUSTERING_STRATEGY)client_data;

  if (gal.iteration_nr > 0)
    Warning(NULL, "Reinitialize the radiance computations");
}

static void ReallocScratchFBSizeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (gal.scratch && gal.iteration_nr > 0) {
    ScratchTerminate();
    ScratchInit();
  }
}

static void RcvCubatureCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  SetCubatureRules(&gal.rcv3rule, &gal.rcv4rule, (CUBATURE_DEGREE)client_data);
}

static void SrcCubatureCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  SetCubatureRules(&gal.src3rule, &gal.src4rule, (CUBATURE_DEGREE)client_data);
}

static void ErrorNormCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  gal.error_norm = (ERROR_NORM)client_data;
}

static void BasisTypeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  BASIS_TYPE old_basis_type = gal.basis_type;
  gal.basis_type = (BASIS_TYPE)client_data;
  if (gal.basis_type != old_basis_type)
    ReallocCoefficients();
}

typedef enum {HIERARCHICAL_REFINEMENT, IMPORTANCE, CLUSTERED, LAZY_LINKING, CONSTANT_RADIANCE, AMBIENT_RADIANCE, EXACT_VISIBILITY, MULTIRES_VISIBILITY} BOOLEAN_OPTION;

static void BooleanOptionsCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  int set = (((XmToggleButtonCallbackStruct *)call_data)->set == XmSET);

  switch ((BOOLEAN_OPTION)client_data) {
  case HIERARCHICAL_REFINEMENT:
    gal.hierarchical = set;
    break;
  case IMPORTANCE:
    gal.importance_driven = set;
    break;
  case CLUSTERED:
    gal.clustered = set;
    if (gal.iteration_nr > 0)
      Warning(NULL, "Reinitialize the radiance computations");
    break;
  case LAZY_LINKING:
    gal.lazy_linking = set;
    break;
  case EXACT_VISIBILITY:
    gal.exact_visibility = set;
    break;
  case MULTIRES_VISIBILITY:
    gal.multires_visibility = set;
    break;
  case CONSTANT_RADIANCE:
    gal.use_constant_radiance = set;
    break;
  case AMBIENT_RADIANCE:
    gal.use_ambient_radiance = set;
    PatchListIterate(Patches, PatchRecomputeColor);
    RenderScene();
    break;
  default:
    Fatal(2, "BooleanOptionsCallback", "Invalid client data %d", (int)client_data);
  }
}

static void Dismiss(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtUnmanageChild(w);
}

static void Reinit(Widget w, XtPointer client_data, XtPointer call_data)
{
  if (CanvasGetMode() != CANVASMODE_WORKING) {
    CanvasPushMode(CANVASMODE_WORKING);
    SetRadianceMethod(&GalerkinRadiosity);
    CanvasPullMode();
    RenderScene();
  } else 
    Error(NULL, "Interrupt computations first before re-initializing");
}

static void ReRender(Widget w, XtPointer client_data, XtPointer call_data)
{
  GalerkinRaycast((FILE *)NULL);
}

void CreateGalerkinControlPanel(void *parent_widget)
{
  Widget form, form1, form2, subform, options, frame, temp;

  galerkinControlPanel = CreateDialog((Widget)parent_widget, "galerkinControlPanel");  
  form = CreateRowColumn(galerkinControlPanel, "galerkinControlForm");
  

  /* two columns next to each other */
  form1 = CreateRowColumn(form, "galerkinControlForm1");
  form2 = CreateRowColumn(form, "galerkinControlForm2");

  /* Galerkin control dialog title label */
  CreateLabel(form1, "galerkinControlTitle");

  /* iteration method frame */
  frame = CreateFrame(form1, "galerkinIterationMethodFrame", "galerkinIterationMethodTitle");
  subform = CreateRadioBox(frame, "galerkinIterationMethodMenu");
  CreateToggleButton(subform, "galJacobiButton",
		     (gal.iteration_method == JACOBI),
		     IterationMethodCallback, (XtPointer)JACOBI);
  CreateToggleButton(subform, "galGaussSeidelButton",
		     (gal.iteration_method == GAUSS_SEIDEL),
		     IterationMethodCallback, (XtPointer)GAUSS_SEIDEL);
  CreateToggleButton(subform, "galSouthwellButton",
		     (gal.iteration_method == SOUTHWELL),
		     IterationMethodCallback, (XtPointer)SOUTHWELL);
  XtManageChild(subform);

  /* basis frame */
  frame = CreateFrame(form1, "galerkinBasisFrame", "galerkinBasisTitle");
  subform = CreateRowColumn(frame, "galerkinBasisForm");
  options = CreateOptionMenu(subform, "galerkinBasisMenu", "galerkinBasisOptions");
  CreateOptionButton(options, "galConstantBasisButton", (gal.basis_type == CONSTANT), 
		     BasisTypeCallback, (XtPointer)CONSTANT);
  CreateOptionButton(options, "galLinearBasisButton", (gal.basis_type == LINEAR), 
		     BasisTypeCallback, (XtPointer)LINEAR);
  CreateOptionButton(options, "galQuadraticBasisButton", (gal.basis_type == QUADRATIC), 
		     BasisTypeCallback, (XtPointer)QUADRATIC);
  CreateOptionButton(options, "galCubicBasisButton", (gal.basis_type == CUBIC), 
		     BasisTypeCallback, (XtPointer)CUBIC);
  XtManageChild(subform);

  /* shaftculling mode frame */
  frame = CreateFrame(form2, "galerkinShaftCullModeFrame", "galerkinShaftCullModeTitle");
  subform = CreateRadioBox(frame, "galerkinShaftCullModeMenu");
  CreateToggleButton(subform, "galShaftCullNeverButton",
		     (gal.shaftcullmode == NEVER_DO_SHAFTCULLING),
		     ShaftCullModeCallback, (XtPointer)NEVER_DO_SHAFTCULLING);
  CreateToggleButton(subform, "galShaftCullRefineButton",
		     (gal.shaftcullmode == DO_SHAFTCULLING_FOR_REFINEMENT),
		     ShaftCullModeCallback, (XtPointer)DO_SHAFTCULLING_FOR_REFINEMENT);
  CreateToggleButton(subform, "galShaftCullAlwaysButton",
		     (gal.shaftcullmode == ALWAYS_DO_SHAFTCULLING),
		     ShaftCullModeCallback, (XtPointer)ALWAYS_DO_SHAFTCULLING);
  XtManageChild(subform);

  /* cubature rules frame */
  frame = CreateFrame(form2, "galerkinCubatureFrame", "galerkinCubatureTitle");
  subform = CreateRowColumn(frame, "galerkinCubatureForm");

  options = CreateOptionMenu(subform, "galerkinRcvCubatureMenu", "galerkinRcvCubatureOptions");
  CreateOptionButton(options, "galCrDeg1button", (gal.rcv_degree == DEGREE_1), RcvCubatureCallback, (XtPointer)DEGREE_1);
  CreateOptionButton(options, "galCrDeg2button", (gal.rcv_degree == DEGREE_2), RcvCubatureCallback, (XtPointer)DEGREE_2);
  CreateOptionButton(options, "galCrDeg3button", (gal.rcv_degree == DEGREE_3), RcvCubatureCallback, (XtPointer)DEGREE_3);
  CreateOptionButton(options, "galCrDeg4button", (gal.rcv_degree == DEGREE_4), RcvCubatureCallback, (XtPointer)DEGREE_4);
  CreateOptionButton(options, "galCrDeg5button", (gal.rcv_degree == DEGREE_5), RcvCubatureCallback, (XtPointer)DEGREE_5);
  CreateOptionButton(options, "galCrDeg6button", (gal.rcv_degree == DEGREE_6), RcvCubatureCallback, (XtPointer)DEGREE_6);
  CreateOptionButton(options, "galCrDeg7button", (gal.rcv_degree == DEGREE_7), RcvCubatureCallback, (XtPointer)DEGREE_7);
  CreateOptionButton(options, "galCrDeg8button", (gal.rcv_degree == DEGREE_8), RcvCubatureCallback, (XtPointer)DEGREE_8);
  CreateOptionButton(options, "galCrDeg9button", (gal.rcv_degree == DEGREE_9), RcvCubatureCallback, (XtPointer)DEGREE_9);
  CreateOptionButton(options, "galCrDeg3Pbutton", (gal.rcv_degree == DEGREE_3_PROD), RcvCubatureCallback, (XtPointer)DEGREE_3_PROD);
  CreateOptionButton(options, "galCrDeg5Pbutton", (gal.rcv_degree == DEGREE_5_PROD), RcvCubatureCallback, (XtPointer)DEGREE_5_PROD);
  CreateOptionButton(options, "galCrDeg7Pbutton", (gal.rcv_degree == DEGREE_7_PROD), RcvCubatureCallback, (XtPointer)DEGREE_7_PROD);

  options = CreateOptionMenu(subform, "galerkinSrcCubatureMenu", "galerkinSrcCubatureOptions");
  CreateOptionButton(options, "galCrDeg1button", (gal.src_degree == DEGREE_1), SrcCubatureCallback, (XtPointer)DEGREE_1);
  CreateOptionButton(options, "galCrDeg2button", (gal.src_degree == DEGREE_2), SrcCubatureCallback, (XtPointer)DEGREE_2);
  CreateOptionButton(options, "galCrDeg3button", (gal.src_degree == DEGREE_3), SrcCubatureCallback, (XtPointer)DEGREE_3);
  CreateOptionButton(options, "galCrDeg4button", (gal.src_degree == DEGREE_4), SrcCubatureCallback, (XtPointer)DEGREE_4);
  CreateOptionButton(options, "galCrDeg5button", (gal.src_degree == DEGREE_5), SrcCubatureCallback, (XtPointer)DEGREE_5);
  CreateOptionButton(options, "galCrDeg6button", (gal.src_degree == DEGREE_6), SrcCubatureCallback, (XtPointer)DEGREE_6);
  CreateOptionButton(options, "galCrDeg7button", (gal.src_degree == DEGREE_7), SrcCubatureCallback, (XtPointer)DEGREE_7);
  CreateOptionButton(options, "galCrDeg8button", (gal.src_degree == DEGREE_8), SrcCubatureCallback, (XtPointer)DEGREE_8);
  CreateOptionButton(options, "galCrDeg9button", (gal.src_degree == DEGREE_9), SrcCubatureCallback, (XtPointer)DEGREE_9);
  CreateOptionButton(options, "galCrDeg3Pbutton", (gal.src_degree == DEGREE_3_PROD), SrcCubatureCallback, (XtPointer)DEGREE_3_PROD);
  CreateOptionButton(options, "galCrDeg5Pbutton", (gal.src_degree == DEGREE_5_PROD), SrcCubatureCallback, (XtPointer)DEGREE_5_PROD);
  CreateOptionButton(options, "galCrDeg7Pbutton", (gal.src_degree == DEGREE_7_PROD), SrcCubatureCallback, (XtPointer)DEGREE_7_PROD);

  XtManageChild(subform);

  /* accuracy control frame */
  frame = CreateFrame(form2, "galerkinAccuracyFrame", "galerkinAccuracyTitle");
  subform = CreateRowColumn(frame, "galerkinAccuracyForm");
  /*
  options = CreateOptionMenu(subform, "galerkinErrorNormMenu", "galerkinErrorNormOptions");
  CreateOptionButton(options, "galRadianceNormButton", (gal.error_norm == RADIANCE_ERROR), ErrorNormCallback, (XtPointer)RADIANCE_ERROR);
  CreateOptionButton(options, "galPowerNormButton", (gal.error_norm == POWER_ERROR), ErrorNormCallback, (XtPointer)POWER_ERROR);
  */

  CreateFormEntry(subform, "galLinkErrorLabel", "galLinkErrorTextf",
		  FET_FLOAT, (XtPointer)&gal.rel_link_error_threshold, NULL, 0);
  CreateFormEntry(subform, "galMinElemAreaLabel", "galMinElemAreaTextf",
		  FET_FLOAT, (XtPointer)&gal.rel_min_elem_area, NULL, 0);

  XtManageChild(subform);

  /* visibility options frame */
  /*
  frame = CreateFrame(form2, "galerkinVisibilityFrame", "galerkinVisibilityTitle");
  subform = CreateRowColumn(frame, "galerkinVisibilityForm");
  
  CreateToggleButton(subform, "galExactVisibilityButton",
		     gal.exact_visibility,
		     BooleanOptionsCallback, (XtPointer)EXACT_VISIBILITY);
  CreateToggleButton(subform, "galMultiResVisibilityButton",
		     gal.multires_visibility,
		     BooleanOptionsCallback, (XtPointer)MULTIRES_VISIBILITY);

  XtManageChild(subform);
  */

  /* other options frame */
  frame = CreateFrame(form1, "galerkinVariaFrame", "galerkinVariaTitle");
  subform = CreateRowColumn(frame, "galerkinVariaForm");
  CreateToggleButton(subform, "galHRButton",
		     gal.hierarchical,
		     BooleanOptionsCallback, (XtPointer)HIERARCHICAL_REFINEMENT);
  CreateToggleButton(subform, "galLazyLinkingButton",
		     gal.lazy_linking,
		     BooleanOptionsCallback, (XtPointer)LAZY_LINKING);
  CreateToggleButton(subform, "galClusteredButton",
		     gal.clustered,
		     BooleanOptionsCallback, (XtPointer)CLUSTERED);
  CreateToggleButton(subform, "galImportanceButton",
		     gal.importance_driven,
		     BooleanOptionsCallback, (XtPointer)IMPORTANCE);
  /*
  CreateToggleButton(subform, "galConstantRadianceButton",
		     gal.use_constant_radiance,
		     BooleanOptionsCallback, (XtPointer)CONSTANT_RADIANCE);
  */
  CreateToggleButton(subform, "galAmbientRadianceButton",
		     gal.use_ambient_radiance,
		     BooleanOptionsCallback, (XtPointer)AMBIENT_RADIANCE);
  XtManageChild(subform);

  /* clustering frame */
  frame = CreateFrame(form1, "galerkinClusteringFrame", "galerkinClusteringTitle");
  subform = CreateRadioBox(frame, "galerkinClusteringStrategyMenu");
  CreateToggleButton(subform, "galClusIsotropicButton",
		     (gal.clustering_strategy == ISOTROPIC),
		     ClusteringStrategyCallback, (XtPointer)ISOTROPIC);
  CreateToggleButton(subform, "galClusOrientedButton",
		     (gal.clustering_strategy == ORIENTED),
		     ClusteringStrategyCallback, (XtPointer)ORIENTED);
  CreateToggleButton(subform, "galClusZVisibilityButton",
		     (gal.clustering_strategy == Z_VISIBILITY),
		     ClusteringStrategyCallback, (XtPointer)Z_VISIBILITY);
  XtManageChild(subform);

  /* scratch renderer frame */
  frame = CreateFrame(form2, "galerkinScratchRendererFrame", "galerkinScratchRendererTitle");
  subform = CreateRowColumn(frame, "galerkinScratchRendererForm");
  CreateFormEntry(subform, "galScratchFBSizeLabel", "galMaxScratchFBSizeTextf",
		  FET_INTEGER, (XtPointer)&gal.scratch_fb_size, NULL, 0);
  CreatePushButton(subform, "galScratchFBReallocButton", ReallocScratchFBSizeCallback, (XtPointer)NULL);
  XtManageChild(subform);

  XtManageChild(form1);
  XtManageChild(form2);
  XtManageChild(form);

  XtAddCallback(galerkinControlPanel, XmNokCallback, Dismiss, (XtPointer)NULL);

  /* unmanage the cancel button */
  temp = XmMessageBoxGetChild(galerkinControlPanel, XmDIALOG_CANCEL_BUTTON);
  XtUnmanageChild(temp);

  CreatePushButton(galerkinControlPanel, "galerkinReinitButton", Reinit, (XtPointer)NULL);
  CreatePushButton(galerkinControlPanel, "galerkinRerenderButton", ReRender, (XtPointer)NULL);
}

void ShowGalerkinControlPanel(void)
{
  if (galerkinControlPanel)
    XtManageChild(galerkinControlPanel);
  else
    Error("ShowGalerkinControlPanel", "Control panel not created yet");
}

void HideGalerkinControlPanel(void)
{
  if (galerkinControlPanel)
    XtUnmanageChild(galerkinControlPanel);
  else
    Error("HideGalerkinControlPanel", "Control panel not created yet");
}
