/* This file is part of `mogul'
 * Copyright (c) 1995 David Deaven
 *
 * In the following, "you" and "You" refer to any entity other than 
 * the Microsoft corporation and its subsidiaries.  All rights are
 * reserved by the author in connection with Microsoft and its
 * subsidiaries.  The Microsoft Network is explicity denied the right
 * to copy, reproduce, or transmit this software.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Author: deaven $
 * $Id: xmogul.c,v 2.0 1996/07/09 20:48:28 deaven Exp deaven $
 */

/* Main program for mogul
 * Copyright (c) 1995 David Deaven
 */

#include <stdio.h>
#include <string.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Core.h>

#include "dmd/tools.h"
#include "dmd/memory.h"
#include "dmd/geometry.h"
#include "dmd/neighbor.h"
#include "xmogul.h"

#define RCFILE ".mogulrc"
#define PATHENV "MOGULPATH"

static char *usage =
  "usage: %s <options> [geometry] [X11/Xt options]\n"
  "options:\n"
  "\t--init=file      read script from file (default .mogulrc or internal)\n"
  "\t--noinit         do not read script, nor use internal default\n"
  "\t--device=name    use `name' as default device instead of x11\n"
  "\t--movie          cycle through all geometries in the input\n"          
  "\t--help           print this message\n"
  ;

static char *default_init = 
  "# Default plot\n"
  "camera position = <geometry center> + [0,0,100]\n"
  "camera view = [0,0,-100]\n"
  "camera up = [0,1,0]\n"
  "add atom <all>\n"
  "add bond <all>\n"
  "attributes(plot) = {size = 3 * plotExtent()}\n"
  ;

int verbose = 0;

static String fallbacks[] = {
#include "app-defaults.h"
NULL
};

struct inputs {
	char *myname;
	char *deviceName;
	char *deviceInfo;
	char *script;
	char *geometryFile;
	int movie;
};

static char *string_from_file(char *s, char *path)
{
	char buf[256];
	FILE *f = fopen(path, "r");
	if(f) {
	  while(fgets(buf, 256, f)) {
	    if(s == NULL) {
	      s = mymalloc((strlen(buf) + 1) * sizeof(char));
	      s[0] = '\0';
	    }
	    else
	      s = myrealloc(s, (strlen(s) + strlen(buf) + 1) * sizeof(char));
	    strcat(s, buf);
	  }
	  fclose(f);
	}
	return s;
}

static char *isoption(char *option, char *s)
{
	if(strncmp(s, option, strlen(option)) == 0)
	  return (s + strlen(option));
	else
	  return NULL;
}

static void do_options(struct inputs *in, int argc, char **argv)
{
	int arg;
	int do_std_init = 1;
	char *value;

	for(arg=1; arg<argc; ++arg)
	{
	  if((value = isoption("--init=", argv[arg])))
	  {
	    char *path = findfile(value, "@" PATHENV);
	    if(path) 
	      in->script = string_from_file(in->script, path);
	    else {
	      fprintf(stderr, "%s: cannot open `%s'\n", in->myname, argv[arg]);
	      exit(1);
	    }
	  }
	  else if((value = isoption("--noinit", argv[arg])))
	  {
	    do_std_init = 0;
	  }
	  else if((value = isoption("--movie", argv[arg])))
	  {
	    in->movie = 1;
	  }
	  else if((value = isoption("--device=", argv[arg])))
	  {
	    char *buf = mystrdup(value), *s, *t;
	    if((t = strchr(buf, ':'))) 
	    {
	      *(t++) = '\0';
	      for(s = t; *s != '\0'; s++) if(*s == ':') *s = ' ';
	      in->deviceInfo = mystrdup(t);
	    }
	    in->deviceName = mystrdup(buf);

	    myfree(buf);
	  }
	  else if(isoption("--help", argv[arg]) ||
		  isoption("-V", argv[arg]))
	  {
	    fprintf(stderr, usage, in->myname);
	    MogulPrintDevices(stderr);
	    exit(0);
	  }
	  else if(argv[arg][0] != '-')
	  {
	    in->geometryFile = argv[arg];
	  }
	}

	if(!in->script && do_std_init)
	{
	  char *path = findfile(RCFILE, "@" PATHENV);
	  if(path) 
	    in->script = string_from_file(NULL, path);
	  else
	    in->script = mystrdup(default_init);
	}
}

static void set_resources(Display *display)
{
	int i;
	XrmDatabase database = XrmGetDatabase(display);

	for(i = 0; fallbacks[i] != NULL; ++i)
	  XrmPutLineResource(&database, fallbacks[i]);

	XrmSetDatabase(display, database);
}

static int x_main(struct inputs *in, int *argc, char **argv)
{
	XtAppContext app;
	Widget toplevel;
	MogulPanel mp;

#if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 6)
	toplevel = XtOpenApplication(&app, APPCLASS,
	  NULL, 0, argc, argv, fallbacks,
	  applicationShellWidgetClass, NULL, 0);
#else
	/* Until release 6, there's no XtOpenApplication(), so we use
	 * our own version of it.
	 */
	XtToolkitInitialize();
	app = XtCreateApplicationContext();
	{ Display *display =
	  XtOpenDisplay(app, NULL, NULL, APPCLASS, NULL, 0, argc, argv);
	  toplevel = XtAppCreateShell(NULL, APPCLASS,
	    applicationShellWidgetClass, display, NULL, 0);
	}
#endif
	set_resources(XtDisplay(toplevel));
	      
	mp = MPCreatePanel(app, toplevel);

	XtRealizeWidget(toplevel);

	if(in->geometryFile && MPLoadFile(mp, in->geometryFile) < 0) {
	  fprintf(stderr, "%s: cannot load `%s'\n", in->myname, in->geometryFile);
	  fprintf(stderr, usage, in->myname);
	  exit(1);
	}
	if(in->script) MPSetProgram(mp, in->script);

	XtAppMainLoop(app);

	return 0;
}

static int s_main(struct inputs *in, int *argc, char **argv)
{
	Geometry *g;
	struct Gfile *gf;
	MogulPlot plot = NewMogulPlot(in->myname);
	MogulParser parser = NewMogulParser();
	MogulDevice device;
	NeighborList *nl = nlalloc(1.e10);

	device = MogulGetDevice(in->deviceName, in->deviceInfo);
	if(!device) MogulPerror("getting device");

	if(!in->geometryFile) in->geometryFile = "-";

	if((gf = GFileOpen(in->geometryFile)))
	{
	  while((g = GFileRead(gf)))
	  {
	    newnl(g, nl);

	    if(in->script) {
	      if(MogulParseString(parser, in->script, plot, device, g, nl)) {
		MogulPerror("executing initialization script");
	      }
	    }

	    if(MogulRender(plot, device)) {
	      MogulPerror("rendering");
	    }

	    if(!in->movie) break;
	  }

	  GFileClose(gf);
	}
	else
	{
	  fprintf(stderr, "%s: cannot load `%s'\n", in->myname, in->geometryFile);
	  fprintf(stderr, usage, in->myname);
	  exit(1);
	}

	nlfree(nl);
	DeleteMogulParser(parser);
	DeleteMogulPlot(plot);

	return 0;
}

void main(int argc, char **argv)
{
	int status = 0;
	struct inputs in;

	ColorAddDatabase(X11_RGB_DATABASE);
	MogulDeviceInit();

	in.myname = argv[0];
	in.deviceName = NULL;
	in.deviceInfo = NULL;
	in.geometryFile = NULL;
	in.script = NULL;
	in.movie = 0;

	do_options(&in, argc, argv);

	if(in.deviceName)
	  status = s_main(&in, &argc, argv);
	else
	  status = x_main(&in, &argc, argv);

	exit(status);
}

#if defined(DEBUG) && defined(TRACKMEM)
void debugMemory(void)
{
	mymemreport(stdout);
}
#endif
