// ----------------------------------------------------------------------------
//
// Copyright (C) Columbia University, 1994. All Rights Reserved.
// Sameer A. Nene, Shree K. Nayar, Hiroshi Murase
//
// See file LICENSE for details of software license agreement.
//
// ----------------------------------------------------------------------------
//
// xmanifoldut.C
//
// Author:               Sameer Nene
// Date:                 09/12/94
// Version:              1.0.0
// Modification History:
//   04/10/96: Fixed scoping problem to work with new ANSI C++ definition
//
// Notes:
//   X module for visualization/manipulation of manifolds/projections
//   
// ----------------------------------------------------------------------------

#define NO_XMSTRINGS

#include <X11/PEX5/PEXlib.h>
#include <Xm/FileSB.h>
#include <math.h>
#include <string.h>
#include <values.h>
#include "errorscope.h"
#include "fileiter.h"
#include "naming.h"
#include "list.h"
#include "vector.h"
#include "dataset.h"
#include "interpolation.h"
#include "ninterpolation.h"
#include "ndataset.h"
#include "xmanifold.h"

void fitToExtents();

void translate(const Vector &v, PEXCoord *pt)
{
  pt -> x = v.length() > Global::d_dim1 ? v.element(Global::d_dim1) : 0.;
  pt -> y = v.length() > Global::d_dim2 ? v.element(Global::d_dim2) : 0.;
  pt -> z = v.length() > Global::d_dim3 ? v.element(Global::d_dim3) : 0.;
}

PEXCoord transform(const PEXCoord &pt, const ExposeCbLocal &data)
{
  PEXCoord t;

  t.x = pt.x * data.d_sx + data.d_tx2;
  t.y = pt.y * data.d_sy + data.d_ty2;
  t.z = pt.z * data.d_sz + data.d_tz2;
  
  return t;
}

PEXColorRGB allocColor()
{
  int t = Global::d_colIndex;
  Global::d_colIndex = (Global::d_colIndex + 1) % Global::NUM_STATIC_COLORS;
  return Global::d_stColors[t];
}

void createLabels(const PEXStructure &sid)
{
  static PEXCoord ap[] = {{0., -1., 1.}, {-1., 0., 1.}, {1., -1., 0.}};
  static PEXCoord offset[] = {{0., -.05, 0.}, {-.05, 0., 0.}, {.05, 0., 0.}};
  PEXCoord sap;
  PEXCoord soffset;
  PEXColor color;
  ExposeCbLocal *data;
  char buf[256];
  int l;

  XtVaGetValues(Global::d_darea, XmNuserData, &data, NULL);  

  PEXSetViewIndex(Global::d_display, sid, PEXOCStore, 1);
  PEXSetATextHeight(Global::d_display, sid, PEXOCStore, .015);
  color.rgb.red = 1.;
  color.rgb.green = 1.;
  color.rgb.blue = 0.;
  PEXSetTextColor(Global::d_display, sid, PEXOCStore, PEXColorTypeRGB, &color);
  sprintf(buf, "e%u", Global::d_dim1);
  l = strlen(buf) - 1;
  sap = transform(ap[0], *data); 
  // Warning: Following dependant on value of offset.
  soffset.x = offset[0].x * l;
  soffset.y = offset[0].y * l;
  soffset.z = offset[0].z * l;
  PEXAnnotationText(Global::d_display, sid, PEXOCStore, &sap, &soffset, l + 1,
		    buf);
  sprintf(buf, "e%u", Global::d_dim2);
  l = strlen(buf) - 1;
  sap = transform(ap[1], *data);
  // Warning: Following dependant on value of offset.
  soffset.x = offset[1].x * l;
  soffset.y = offset[1].y * l;
  soffset.z = offset[1].z * l;
  PEXAnnotationText(Global::d_display, sid, PEXOCStore, &sap, &soffset, l + 1,
		    buf);
  sprintf(buf, "e%u", Global::d_dim3);
  l = strlen(buf) - 1;
  sap = transform(ap[2], *data);
  // Warning: Following dependant on value of offset.
  soffset.x = offset[2].x * l;
  soffset.y = offset[2].y * l;
  soffset.z = offset[2].z * l;
  PEXAnnotationText(Global::d_display, sid, PEXOCStore, &sap, &soffset, l + 1,
		    buf);
}

void createAxis(const PEXStructure &sid)
{
  static PEXCoord faces[4][5] = {{{-1., 1., 1.}, {1., 1., 1.}, {1., -1., 1.},
				  {-1., -1., 1.}, {-1., 1., 1.}},
				 {{-1., 1., -1.}, {1., 1., -1.}, {1., -1., -1.},
				  {-1., -1., -1.}, {-1., 1., -1.}},
				 {{-1., 1., 1.}, {1., 1., 1.}, {1., 1., -1.},
				  {-1., 1., -1.}, {-1., 1., 1.}},
				 {{-1., -1., 1.}, {1., -1., 1.}, {1., -1., -1.},
				  {-1., -1., -1.}, {-1., -1., 1.}}};
  int i, j;
  PEXCoord side[4][5];
  PEXColor color;
  PEXListOfVertex cube[4];
  ExposeCbLocal *data;

  XtVaGetValues(Global::d_darea, XmNuserData, &data, NULL);

  PEXSetViewIndex(Global::d_display, sid, PEXOCStore, 1);
  color.rgb.red = 1.;
  color.rgb.green = 1.;
  color.rgb.blue = 0.;
  PEXSetLineColor(Global::d_display, sid, PEXOCStore, PEXColorTypeRGB, &color);
  PEXSetLineWidth(Global::d_display, sid, PEXOCStore, 1.);
  PEXSetLineType(Global::d_display, sid, PEXOCStore, PEXLineTypeSolid);
  PEXSetPolylineInterpMethod(Global::d_display, sid, PEXOCStore,
			     PEXPolylineInterpNone);
  for(i = 0; i < 4; ++i) {
    cube[i].count = 5;
    for(j = 0; j < 5; ++j)
      side[i][j] = transform(faces[i][j], *data);
    cube[i].vertices.no_data = side[i];
  }
  PEXPolylineSetWithData(Global::d_display, sid, PEXOCStore, PEXGANone,
			 PEXColorTypeRGB, 4, cube);
}

void adjustRange(const PEXCoord &pt, PEXCoord *d_min, PEXCoord *d_max)
{
  PEXCoord &min = *d_min, &max = *d_max;

  if(pt.x < min.x)
    min.x = pt.x;
  if(pt.x > max.x)
    max.x = pt.x;
  if(pt.y < min.y)
    min.y = pt.y;
  if(pt.y > max.y)
    max.y = pt.y;
  if(pt.z < min.z)
    min.z = pt.z;
  if(pt.z > max.z)
    max.z = pt.z;
}  

void setViewingParam(NamedInterpolation *nip)
{
  int i, n;

  Vector param(n = nip -> interpolation() -> dimension());
  for(i = 0; i < n; param.element(i++, .5)); 
  
  switch(n) {
  case 1:
    nip -> setSamplingFreq(100, 0);
    nip -> setDimensionMap(0, 0);
    nip -> setSamplingParams(param);
    break;
  default:
    nip -> setSamplingFreq(30, 30);
    nip -> setDimensionMap(0, 1);
    nip -> setSamplingParams(param);
  }
}

int compute_view(double theta, double phi, double alpha, double tx, double ty,
		 double tz, PEXMatrix ori)
{
  PEXCoord ref;
  PEXVector up, dir;

  ref.x = tx;
  ref.y = ty;
  ref.z = tz;

  up.x = sin(alpha);
  up.y = cos(alpha);
  up.z = 0.;

  dir.x = cos(phi) * sin(theta);
  dir.y = sin(phi);
  dir.z = cos(phi) * cos(theta);
  
  return PEXViewOrientationMatrix(&ref, &dir, &up, ori);
}

void objectSearchProc(Widget w, XtPointer p1, XtPointer)
{
  char *p2, *p3;
  int f, i, j;
  XmString *files;
  
  XmStringGetLtoR(((XmFileSelectionBoxCallbackStruct *)p1) -> dir,
		  XmSTRING_DEFAULT_CHARSET, &p3);
  FileList fl(p3, NULL, "vec");
  XtFree(p3);
  FileListIter it(fl);
  List<char* > namelist;
  for(i = 0; it; ++it) {
    p2 = strrchr(it(), '/');
    if(p2 != 0) {
      j = NamingScope::getNameLength(p2 + 1);
      p3 = strncpy(new char[j + 1], p2 + 1, j);
      p3[j] = '\0';
      
    }
    else {
      j = NamingScope::getNameLength(it());
      p3 = strncpy(new char[j + 1], it(), j);
      p3[j] = '\0';
    }
    ListIter<char* > it2(namelist);
    for(f = 0; it2; ++it2)
      if(strcmp(it2(), p3) == 0) {
	f = 1;
	break;
      }
    if(f == 0) {
      namelist += p3;
      ++i;
    }
    else
      delete[] p3;
  }
  ListIter<char* > it2(namelist);
  for(j = 0, files = new XmString[i]; it2; ++it2, ++j)
    files[j] = XmStringCreateSimple(it2());
  XtVaSetValues(w,
		XmNfileListItems, files,
		XmNfileListItemCount, i,
		XmNdirSpec, NULL,
		XmNlistUpdated, True,
		NULL);
  for(ListIter<char* > it3(namelist); it3; ++it3) {
    XmStringFree(files[--i]);
    delete[] it3();
  }
  delete[] files;
}

void fillList(Widget listbox, const List<NamedDataSet* > &list)
{
  int i;
  XmString *names;

  ListIter<NamedDataSet* > it(list);
  for(i = 0, names = new XmString[list.length()]; it; ++it, ++i)
    names[i] = XmStringCreateSimple((char *)(it() -> dataName()));
  XtVaSetValues(listbox, XmNitems, names, XmNitemCount, i, NULL);

  while(i--)
    XmStringFree(names[i]);
  delete[] names;
}

void fillList(Widget listbox, const List<NamedInterpolation* > &list)
{
  int i;
  XmString *names;

  ListIter<NamedInterpolation* > it(list);
  for(i = 0, names = new XmString[list.length()]; it; ++it, ++i)
    names[i] = XmStringCreateSimple((char *)(it() -> dataName()));
  XtVaSetValues(listbox, XmNitems, names, XmNitemCount, i, NULL);
  
  while(i--)
    XmStringFree(names[i]);
  delete[] names;
}

void addList(List<NamedDataSet* > *list, const NamedDataSet &ds)
{
  ListManip<NamedDataSet* > m(list);
  for(; m; ++m)
    if(strcmp(m() -> dataName(), ds.dataName()) == 0) {
      delete m();
      m.remove();
      break;
    }
  m.insert((NamedDataSet *)&ds);
}

void addList(List<NamedInterpolation* > *list, const NamedInterpolation &ip)
{
  ListManip<NamedInterpolation* > m(list);
  for(; m; ++m)
    if(strcmp(m() -> dataName(), ip.dataName()) == 0) {
      delete m();
      m.remove();
      break;
    }
  m.insert((NamedInterpolation *)&ip);
}

void redraw()
{
  ExposeCbLocal *data;

  XtVaGetValues(Global::d_darea, XmNuserData, &data, NULL);
  XClearWindow(Global::d_display, Global::d_drawin);
  PEXRenderNetwork(Global::d_display, Global::d_drawin,
		   data -> renderer, data -> axisSid);
  if(data -> d_viewLabels == True)
    PEXRenderNetwork(Global::d_display, Global::d_drawin,
		     data -> renderer, data -> labelsSid);
  for(ListIter<NamedDataSet* > it(Global::d_proj); it; ++it)
    if(it() -> getVisibility() == NamedDataSet::VISIBLE)
      PEXRenderNetwork(Global::d_display, Global::d_drawin,
		       data -> renderer, it() -> getPEXStruct());
  for(ListIter<NamedInterpolation* > it2(Global::d_manifold); it2; ++it2)
    if(it2() -> getVisibility() == NamedInterpolation::VISIBLE)
      PEXRenderNetwork(Global::d_display, Global::d_drawin,
		       data -> renderer, it2() -> getPEXStruct());
}

void fitToExtents()
{
  PEXCoord ptmin, ptmax, min, max;
  ExposeCbLocal *data;
  double r, rx, ry, rz, t;

  min.x = min.y = min.z = MAXFLOAT;
  max.x = max.y = max.z = MINFLOAT;
  
  for(ListIter<NamedDataSet* > it1(Global::d_proj); it1; ++it1) {
    NamedDataSet &ds = *(it1());
    if(ds.getVisibility() == NamedDataSet::VISIBLE) {
      ds.getExtents(&ptmin, &ptmax);
      adjustRange(ptmin, &min, &max);
      adjustRange(ptmax, &min, &max);
    }
  }
  for(ListIter<NamedInterpolation* > it2(Global::d_manifold); it2; ++it2) {
    NamedInterpolation &ip = *(it2());
    if(ip.getVisibility() == NamedInterpolation::VISIBLE) {
      ip.getExtents(&ptmin, &ptmax);
      adjustRange(ptmin, &min, &max);
      adjustRange(ptmax, &min, &max);
    }
  }

  XtVaGetValues(Global::d_darea, XmNuserData, &data, NULL);

  rx = max.x - min.x;
  ry = max.y - min.y;
  rz = max.z - min.z;
  r = (t = rx > ry ? rx : ry) > rz ? t : rz;
  data -> d_sx = data -> d_sy = data -> d_sz = r / 2.;
  data -> d_tx2 = (max.x + min.x) / 2.;
  data -> d_ty2 = (max.y + min.y) / 2.;
  data -> d_tz2 = (max.z + min.z) / 2.;

  PEXDestroyStructures(Global::d_display, 1, &(data -> axisSid));
  createAxis(data -> axisSid = PEXCreateStructure(Global::d_display));
  PEXDestroyStructures(Global::d_display, 1, &(data -> labelsSid));
  createLabels(data -> labelsSid = PEXCreateStructure(Global::d_display));

  data -> d_tx = data -> d_tx2;
  data -> d_ty = data -> d_ty2;
  data -> d_tz = data -> d_tz2;
  compute_view(data -> d_theta, data -> d_phi, data -> d_alpha, data -> d_tx,
	       data -> d_ty, data -> d_tz, data -> d_view.orientation);
  data -> d_vwin[0].x = data -> d_vwin[0].y = -r;
  data -> d_vwin[1].x = data -> d_vwin[1].y = r;
  data -> d_prp.z = r * 5.;
  PEXViewMappingMatrix(data -> d_vwin, &(data -> d_vport), True,
		       &(data -> d_prp), 0., -r, r, data -> d_view.mapping);
  PEXSetTableEntries(Global::d_display, data -> d_view_table, 1, 1, PEXLUTView,
		     (PEXPointer)&(data -> d_view));

  data -> d_min = min;
  data -> d_max = max;
}

/* Optimize and fix bugs in change_view later */

void change_view(Widget w, XButtonEvent *event, String *args)
{
  int i, j;
  static Position x, y;
  static PEXMatrix inv_ori;
  ExposeCbLocal *data;
  PEXCoord movement, shift;

  XtVaGetValues(w, XmNuserData, &data, NULL);

  if(strcmp(args[0], "b1m") == 0) {
    data -> d_theta += (x - event -> x) * M_PI / 180.;
    data -> d_phi += (event -> y - y) * M_PI / 180.;
    compute_view(data -> d_theta, data -> d_phi, data -> d_alpha, data -> d_tx,
		 data -> d_ty, data -> d_tz, (data -> d_view).orientation);
    PEXSetTableEntries(Global::d_display, data -> d_view_table, 1, 1,
		       PEXLUTView, (PEXPointer)&(data -> d_view));
    if(Global::d_pexavail == True)
      redraw();
  }
  else if(strcmp(args[0], "b2m") == 0) {
    data -> d_vwin[0].x += (event -> x - x) * data -> d_sx * .01;
    data -> d_vwin[1].x -= (event -> x - x) * data -> d_sx * .01;
    data -> d_vwin[0].y += (event -> x - x) * data -> d_sy * .01;
    data -> d_vwin[1].y -= (event -> x - x) * data -> d_sy * .01;
    PEXViewMappingMatrix(data -> d_vwin, &(data -> d_vport), True,
			 &(data -> d_prp), 0., -(data -> d_sx * 2.),
			 data -> d_sx * 2., data -> d_view.mapping);
    PEXSetTableEntries(Global::d_display, data -> d_view_table, 1, 1,
		       PEXLUTView, (PEXPointer)&(data -> d_view));
    if(Global::d_pexavail == True)
      redraw();
  }
  else if(strcmp(args[0], "b3m") == 0) {
    inv_ori[3][3] = 1.;
    for(i = 0; i < 3; ++i)
      for(j = 0; j < 3; ++j)
	inv_ori[i][j] = (data -> d_view).orientation[j][i];
    movement.x = (x - event -> x) * .01;
    movement.y = (event -> y - y) * .01;
    movement.z = 0.;
    PEXTransformPoints(inv_ori, 1, &movement, &shift);
    data -> d_tx += shift.x * data -> d_sx;
    data -> d_ty += shift.y * data -> d_sy;
    data -> d_tz += shift.z * data -> d_sz;
    compute_view(data -> d_theta, data -> d_phi, data -> d_alpha, data -> d_tx,
		 data -> d_ty, data -> d_tz, (data -> d_view).orientation);
    PEXSetTableEntries(Global::d_display, data -> d_view_table, 1, 1,
		       PEXLUTView, (PEXPointer)&(data -> d_view));
    if(Global::d_pexavail == True)
      redraw();
  }
  
  x = event -> x;
  y = event -> y;
}

void get_path(XmFileSelectionBoxCallbackStruct *p1, char **dir, char **name)
{
  char *p2, *p3, *mdir;
  
  XmStringGetLtoR(((XmFileSelectionBoxCallbackStruct *)p1) -> dir,
		  XmSTRING_DEFAULT_CHARSET, &mdir);
  XmStringGetLtoR(((XmFileSelectionBoxCallbackStruct *)p1) -> value,
		  XmSTRING_DEFAULT_CHARSET, &p2);
  if(*p2 == '/') {
    p3 = strrchr(p2, '/') + 1;
    strcpy(*name = new char[strlen(p3) + 1], p3);
    *p3 = '\0';
    strcpy(*dir = new char[strlen(p2) + 1], p2);
  }
  else {
    if((p3 = strrchr(p2, '/')) == NULL)
      p3 = p2;
    else
      ++p3;
    strcpy(*dir = new char[strlen(mdir) + 1], mdir);
    strcpy(*name = new char[strlen(p3) + 1], p3);
  }
  XtFree(mdir);
  XtFree(p2);
}

void sample(const Interpolation *ip, Vector &v, DataSetIter &it, int level)
{
  int i, s = Global::d_sfreq[level];
  double t, inc;

  if(level == ip -> dimension() - 1) {
    for(i = 0, t = 0., inc = 1. / s; i < s; ++i, t += inc, ++it) {
      v.element(level, t);
      it() = ip -> sample(v);
    }
    v.element(level, 1.);
    it() = ip -> sample(v);
    ++it;
  }
  else {
    for(i = 0, t = 0., inc = 1. / s; i < s; ++i, t += inc) {
      v.element(level, t);
      sample(ip, v, it, level + 1);
    }
    v.element(level, 1.);
    sample(ip, v, it, level + 1);
  }
}

void fillListWithVisible(Widget listbox)
{
  int i, j;
  XmString *names;
  
  ListIter<NamedDataSet* > it1(Global::d_proj);
  for(j = 0; it1; ++it1)
    if(it1() -> getVisibility() == NamedDataSet::VISIBLE)
      ++j;

  for(ListIter<NamedInterpolation* > it2(Global::d_manifold); it2; ++it2)
    if(it2() -> getVisibility() == NamedInterpolation::VISIBLE)
      ++j;

  ListIter<NamedDataSet* > it3(Global::d_proj);
  for(i = 0, names = new XmString[j]; it3; ++it3)
    if(it3() -> getVisibility() == NamedDataSet::VISIBLE)
      names[i++] = XmStringCreateSimple((char *)(it3() -> dataName()));
  
  for(ListIter<NamedInterpolation* > it4(Global::d_manifold); it4; ++it4)
    if(it4() -> getVisibility() == NamedDataSet::VISIBLE)
      names[i++] = XmStringCreateSimple((char *)(it4() -> dataName()));
  
  XtVaSetValues(listbox, XmNitems, names, XmNitemCount, i, NULL);
  
  while(i--)
    XmStringFree(names[i]);
  delete[] names;
}

void fillListWithInvisible(Widget listbox)
{
  int i, j;
  XmString *names;
  
  ListIter<NamedDataSet* > it1(Global::d_proj);
  for(j = 0; it1; ++it1)
    if(it1() -> getVisibility() == NamedDataSet::INVISIBLE)
      ++j;

  for(ListIter<NamedInterpolation* > it2(Global::d_manifold); it2; ++it2)
    if(it2() -> getVisibility() == NamedInterpolation::INVISIBLE)
      ++j;

  ListIter<NamedDataSet* > it3(Global::d_proj);
  for(i = 0, names = new XmString[j]; it3; ++it3)
    if(it3() -> getVisibility() == NamedDataSet::INVISIBLE)
      names[i++] = XmStringCreateSimple((char *)(it3() -> dataName()));
  
  for(ListIter<NamedInterpolation* > it4(Global::d_manifold); it4; ++it4)
    if(it4() -> getVisibility() == NamedInterpolation::INVISIBLE)
      names[i++] = XmStringCreateSimple((char *)(it4() -> dataName()));

  XtVaSetValues(listbox, XmNitems, names, XmNitemCount, i, NULL);

  while(i--)
    XmStringFree(names[i]);
  delete[] names;
}

void desenseMenu()
{
  XtSetSensitive(Global::d_menubar, False);
}

void sensitizeMenu()
{
  XtSetSensitive(Global::d_menubar, True);
}
