/* Copyright (c) Mark J. Kilgard, 1996. */

/* This program is freely distributable without licensing fees 
   and is provided without guarantee or warrantee expressed or 
   implied. This program is -not- in the public domain. */

/* Molecule Viewer user interface */

#include <stdlib.h>
#include <stdio.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/CascadeB.h>
#include <Xm/Frame.h>
#include <Xm/FileSB.h>
#include <Xm/Text.h>
#include <Xm/MessageB.h>
#include <Xm/LabelG.h>
#include <X11/GLw/GLwMDrawA.h>  /* Motif OpenGL drawing area
                                   widget */
#include <GL/glx.h>

#include "molview.h"
#include "sovLayerUtil.h"

static int config[] = {
  None, None,           /* Space for multisampling GLX
                           attributes if supported. */
  GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 12,
  GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
  None
};
static int *dblBuf = &config[2];
static int *snglBuf = &config[3];
static String fallbackResources[] = {
  "*sgiMode: true",     /* Try to enable Indigo Magic look & feel */
  "*useSchemes: all",   /* and SGI schemes. */
  "*title: Molecule Viewer",
  "*filebox_popup*title: Open molecule...",
  "*glxarea*width: 400",
  "*glxarea*height: 300",
  NULL
};
XtActionsRec actionsTable[] = {
  {"startRotation", startRotation},
  {"rotation", rotation},
  {"doPick", doPick}
};

XtAppContext app;
Display *dpy;
Bool doubleBuffer = True, madeCurrent = False;
Widget toplevel, mainw, menubar, menupane, btn, cascade, frame, glxarea, dialog, popup, cmdw;
WidgetList menuWidgets;
Window glxwin;
GLXContext cx;
XVisualInfo *vi = NULL;
Arg args[10];
XFontStruct *fontInfo;
Dimension viewWidth, viewHeight;
char *textLabels[] = {"Atom Name:", "XYZ Position:", "Radius:"};
Widget labels[XtNumber(textLabels)];
Visual *overlayVisual = NULL;
int overlayDepth;
Colormap overlayColormap;

static void
detectOverlaySupport(Display *dpy)
{
  sovVisualInfo template, *overlayVisuals;
  int layer, nVisuals, i, entries;

  /* Need more than two colormap entries for reasonable menus. */
  entries = 2;
  for (layer = 1; layer <= 3; layer++) {
    template.layer = layer;
    template.vinfo.screen = DefaultScreen(dpy);
    overlayVisuals = sovGetVisualInfo(dpy,
      VisualScreenMask | VisualLayerMask, &template, &nVisuals);
    if (overlayVisuals) {
      for (i = 0; i < nVisuals; i++) {
        if (overlayVisuals[i].vinfo.visual->map_entries > entries) {
          overlayVisual = overlayVisuals[i].vinfo.visual;
          overlayDepth = overlayVisuals[i].vinfo.depth;
	  entries = overlayVisual->map_entries;
        }
      }
      XFree(overlayVisuals);
    }
  }
  if (overlayVisual)
    overlayColormap = XCreateColormap(dpy, DefaultRootWindow(dpy),
      overlayVisual, AllocNone);
}

static int
isSupportedByGLX(Display * dpy, char *extension)
{
#if defined(GLX_VERSION_1_1)
  static const char *extensions = NULL;
  const char *start;
  char *where, *terminator;
  int major, minor;

  glXQueryVersion(dpy, &major, &minor);
  /* Be careful not to call glXQueryExtensionsString if it
     looks like the server doesn't support GLX 1.1.
     Unfortunately, the original GLX 1.0 didn't have the notion 

     of GLX extensions. */
  if ((major == 1 && minor >= 1) || (major > 1)) {
    if (!extensions)
      extensions = glXQueryExtensionsString(dpy, DefaultScreen(dpy));
    /* It takes a bit of care to be fool-proof about parsing
       the GLX extensions string.  Don't be fooled by
       sub-strings,  etc. */
    start = extensions;
    for (;;) {
      where = strstr(start, extension);
      if (!where)
        return 0;
      terminator = where + strlen(extension);
      if (where == start || *(where - 1) == ' ') {
        if (*terminator == ' ' || *terminator == '\0') {
          return 1;
        }
      }
      start = terminator;
    }
  }
#else
  /* No GLX extensions before GLX 1.1 */
#endif
  return 0;
}

int
main(int argc, char *argv[])
{
  static XmButtonType buttonTypes[] =
  {
    XmPUSHBUTTON, XmPUSHBUTTON, XmSEPARATOR, XmCHECKBUTTON, XmCHECKBUTTON
  };
  XmString buttonLabels[XtNumber(buttonTypes)];
  int n, i;

  toplevel = XtAppInitialize(&app, "Molview", NULL, 0, &argc, argv,
    fallbackResources, NULL, 0);
  XtAppAddActions(app, actionsTable, XtNumber(actionsTable));
  dpy = XtDisplay(toplevel);

#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
  if (isSupportedByGLX(dpy, "GLX_SGIS_multisample")) {
    config[0] = GLX_SAMPLES_SGIS;
    config[1] = 4;
    vi = glXChooseVisual(dpy, DefaultScreen(dpy), config);
  }
#endif

  if (vi == NULL) {
    /* Find an OpenGL-capable RGB visual with depth buffer. */
    vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
    if (vi == NULL) {
      vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
      if (vi == NULL)
        XtAppError(app, "no RGB visual with depth buffer");
      doubleBuffer = False;
    }
  }
  /* Create an OpenGL rendering context. */
  cx = glXCreateContext(dpy, vi, /* No display list sharing */ None,
  /* Favor direct rendering */ True);
  if (cx == NULL)
    XtAppError(app, "could not create rendering context");
  mainw = XtVaCreateWidget("mainw", xmMainWindowWidgetClass, toplevel,
    XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
    NULL);
  XtManageChild(mainw);

  XtAddEventHandler(toplevel, StructureNotifyMask, False,
    mapStateChanged, NULL);

  /* Try to find a good overlay visual for pulldown and popup menus. */
  detectOverlaySupport(dpy);

  /* Create menu bar. */
  menubar = XmCreateMenuBar(mainw, "menubar", NULL, 0);
  XtManageChild(menubar);
  n = 0;
  if (overlayVisual) {
    XtSetArg(args[n], XmNvisual, overlayVisual);
    n++;
    XtSetArg(args[n], XmNdepth, overlayDepth);
    n++;
    XtSetArg(args[n], XmNcolormap, overlayColormap);
    n++;
  }
  menupane = XmCreatePulldownMenu(menubar, "menupane", args, n);
  if (overlayVisual) {
    XtAddCallback(XtParent(menupane), XmNpopupCallback,
      ensurePulldownColormapInstalled, NULL);
  }
  btn = XmCreatePushButton(menupane, "Open molecule...", NULL, 0);
  XtAddCallback(btn, XmNactivateCallback, openMolecule, NULL);
  XtManageChild(btn);
  btn = XmCreatePushButton(menupane, "Quit", NULL, 0);
  XtAddCallback(btn, XmNactivateCallback, quit, NULL);
  XtManageChild(btn);
  XtSetArg(args[0], XmNsubMenuId, menupane);
  cascade = XmCreateCascadeButton(menubar, "File", args, 1);
  XtManageChild(cascade);

  /* Create framed drawing area for OpenGL rendering. */
  frame = XmCreateFrame(mainw, "frame", NULL, 0);
  XtManageChild(frame);
  glxarea = XtVaCreateManagedWidget("glxarea", glwMDrawingAreaWidgetClass,
    frame, GLwNvisualInfo, vi, NULL);
  XtVaGetValues(glxarea, XtNwidth, &viewWidth, XtNheight, &viewHeight, NULL);
  XtAddCallback(glxarea, XmNexposeCallback, draw, NULL);
  XtAddCallback(glxarea, XmNresizeCallback, resize, NULL);
  XtAddCallback(glxarea, GLwNginitCallback, init, NULL);

  /* Create atom pick result text field. */
  cmdw = XtVaCreateWidget("cmdw", xmRowColumnWidgetClass, mainw,
    XmNpacking, XmPACK_COLUMN,
    XmNnumColumns, XtNumber(textLabels),
    XmNisAligned, True,
    XmNentryAlignment, XmALIGNMENT_END,
    XmNorientation, XmHORIZONTAL,
    NULL);
  for (i = 0; i < XtNumber(textLabels); i++) {
    XtVaCreateManagedWidget(textLabels[i],
      xmLabelGadgetClass, cmdw, NULL);
    labels[i] = XtVaCreateManagedWidget("atom_info", xmTextWidgetClass, cmdw,
      XmNeditable, False,
      XmNcursorPositionVisible, False,
      XmNtraversalOn, False,
      XmNcolumns, 25,
      NULL);
  }
  clearPickInfo();
  XtManageChild(cmdw);

  /* Create popup menu. */
  buttonLabels[0] = XmStringCreateLocalized("Solid");
  buttonLabels[1] = XmStringCreateLocalized("Wireframe");
  buttonLabels[2] = NULL;
  buttonLabels[3] = XmStringCreateLocalized("Spin Momentum");
  buttonLabels[4] = XmStringCreateLocalized("Auto HiRes");
  n = 0;
  XtSetArg(args[n], XmNbuttonCount, XtNumber(buttonTypes)); n++;
  XtSetArg(args[n], XmNbuttons, buttonLabels); n++;
  XtSetArg(args[n], XmNbuttonType, buttonTypes); n++;
  XtSetArg(args[n], XmNbuttonSet, 4); n++;
  XtSetArg(args[n], XmNsimpleCallback, processMenuUse); n++;
  if (overlayVisual) {
    XtSetArg(args[n], XmNvisual, overlayVisual); n++;
    XtSetArg(args[n], XmNdepth, overlayDepth); n++;
    XtSetArg(args[n], XmNcolormap, overlayColormap); n++;
  }
  popup = XmCreateSimplePopupMenu(frame, "popup", args, n);
  XtAddEventHandler(frame, ButtonPressMask, False, activateMenu, &popup);
  XmStringFree(buttonLabels[0]);
  XmStringFree(buttonLabels[1]);
  XmStringFree(buttonLabels[3]);
  XmStringFree(buttonLabels[4]);

  XtVaGetValues(popup, XmNchildren, &menuWidgets, NULL);
  XmToggleButtonSetState(menuWidgets[3], True, False);
  XmToggleButtonSetState(menuWidgets[4], True, False);

  /* Set up application's window layout. */
  XmMainWindowSetAreas(mainw, menubar, cmdw, NULL, NULL, frame);
  XtRealizeWidget(toplevel);

  /* Once widget is realized (ie, associated with a created X
     window), we can bind the OpenGL rendering context to the
     window.  */

  glXMakeCurrent(dpy, XtWindow(glxarea), cx);
  madeCurrent = True;

  /* Get font for reporting no atom loaded. */
  fontInfo = XLoadQueryFont(dpy, "-adobe-helvetica-medium-r-normal--18-*-*-*-p-*-iso8859-1");
  if (fontInfo == NULL) {
    fontInfo = XLoadQueryFont(dpy, "fixed");
    if (fontInfo == NULL)
      XtAppError(app, "no X font available?");
  }
  glXUseXFont(fontInfo->fid, 32, 96, 1024 + 32);
  glListBase(1024);

  XtAppMainLoop(app);
  return 0;
}

/* for Ch only */
/* #ifdef _CH_ */
#pragma import "gui_run.c"
#pragma import "sovlayerutil.c"
#pragma import "pick.c"
#pragma import "mol_file.c"
#pragma import "render.c"
#pragma import "trackball.c"
/* #endif */