#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "X10.h"
#ifdef SVR4
#include <string.h>
#else
#include <strings.h>
#endif
#include "../sim/CVDefs.h"
#include "../data/Parameter.h" /* for get_vardata() */
#include "Xcv.h"

extern Display *dpy;
extern Window root;
extern int screen;
extern unsigned long white_pixel, black_pixel; /* Main.c */
extern double getval();

extern XFontStruct *diagram_font;
extern XContext circuit_data;
extern int addv_next;
extern Vertex vlist[];
extern Cursor left_ptr_cursor;
extern GC invertGC;
extern void mk_icn(), diagram_draw_wires(), diagram_map_icons();
extern XContext assoc_win;
extern Window mk_circuit_diagram(); /* from diagram.c */
/*FORWARD*/ void choose_variable();

Window top_w;
GC labelGC;
static int done;
static unsigned long mask;
static GC imageGC = 0;
int manip_type; /* exported to diagram.c: mk_sys() */
static int *place_ptr, *type_ptr;
/*FORWARD*/ static void getWinList();
static Window Diagram = 0;

static void hilite(), de_hilite(), goof();

modify_parameters(which, mp_place, mp_type)
int which; /* 0: modify independent vars, 1: plot dependent vars,2: plot both*/
int *mp_place, *mp_type;
{
  XEvent event;
  static winCount = 0;
  static Window winList[100] = {(Window) 0, };
  static Pixmap savedUnder = (Pixmap) 0;
  XGCValues xgcv;
  static void handle_ExposeWindow(), handle_EnterWindow(), 
       handle_LeaveWindow(), handle_ButtonPressed();
  XWindowAttributes att;
  int n;

  if (!Diagram) Diagram = mk_circuit_diagram(100, 100);
  else XUnmapSubwindows(dpy, Diagram);
  xgcv.subwindow_mode = IncludeInferiors;
  if (!imageGC) imageGC = XCreateGC(dpy, root, GCSubwindowMode, &xgcv);
  if (!labelGC) {
    xgcv.foreground = white_pixel;
    xgcv.background = black_pixel;
    labelGC = XCreateGC(dpy, root, GCForeground | GCBackground, &xgcv);
  }
  manip_type = which;
  place_ptr = mp_place;
  type_ptr = mp_type;
  mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask | ExposureMask;
  top_w = Diagram;
  XSelectInput(dpy, top_w, mask);
  /* POP-UP! This does a manual save-under, since I can't get the 
   * automatic one to work.
   */
#ifndef NOGRABS
  XGrabServer(dpy); /* Don't let reality change while we've got it saved */
#endif
  XSync(dpy, False); /* Save the most up-to-date picture possible. */

  /* Calculate the footprint of this window. */
  XGetWindowAttributes(dpy, top_w, &att);
  if (!savedUnder) savedUnder = 
    XCreatePixmap(dpy, root, att.width + 2*att.border_width, 
		  att.height + 2*att.border_width, DefaultDepth(dpy,screen));
  XCopyArea(dpy, root, savedUnder, imageGC, att.x, att.y, 
	    att.width+2*att.border_width,att.height+2*att.border_width, 0, 0);
  XMapRaised(dpy, top_w);
  
  if (!winCount) getWinList(top_w, winList, &winCount);
  done = 0;
  while(!done) {
    for (;;) {
      XMaskEvent(dpy, mask, &event);
      for (n=0; n < winCount ; n++)
	 if (((XEnterWindowEvent *)&event)->window == winList[n])
            goto contin;
    }
  contin:
    switch ((int) event.type) {
    case Expose: 
      if (((XExposeEvent *)&event)->count == 0)
	/* Compress multiple rectangle exposure events, using only the last */
	handle_ExposeWindow((XExposeEvent *)&event);
      break;
    case EnterNotify:
      handle_EnterWindow((XEnterWindowEvent *)&event);
      break;
    case LeaveNotify:
      handle_LeaveWindow((XLeaveWindowEvent *)&event);
      break;
    case ButtonPress:
      handle_ButtonPressed((XButtonPressedEvent *)&event);
      break;
    default:
#ifdef DEBUG
      printf("Not handling event! Type: %d\n", event.type);
#endif
      break;
    }
  }
  /* POP DOWN! */
  XUnmapWindow(dpy, Diagram);
  /* Try to avoid redisplays, since we have saved all the bits. */
  XCopyArea(dpy, savedUnder, root, imageGC, 0, 0,
	    att.width + 2*att.border_width, att.height + 2*att.border_width,
	    att.x, att.y);
  XFreePixmap(dpy, savedUnder); savedUnder = (Pixmap) 0;
  {/* Discard all accumulated exposure events. */
    XEvent ev;
    XSync(dpy, False); 
    while (XCheckTypedEvent(dpy, Expose, &ev) == True)
      ; /* discard them since we repainted the screen instead. */
  }
#ifndef NOGRABS
  XUngrabServer(dpy);
#endif
  return;
}

/* This is a recursive, pre-order traversal of all windows with win as
 * anscestor.  The routine builds a list in winList and keeps a count in 
 * countPtr, both returned values.  Space for winList & countPtr must be 
 * allocated by calling fn.
 */
static void getWinList(win, winList, countPtr)
Window win;
Window *winList; /*RETURN*/
int *countPtr;   /*RETURN*/
{
  unsigned int nchildren;
  Window *children, parent, root_return;
  XQueryTree(dpy, win, &root_return, &parent, &children, &nchildren);
  winList[(*countPtr)++] = win;
  XSelectInput(dpy, win, mask);
  while (nchildren--)
    getWinList(children[nchildren], winList, countPtr);
  XFree((char *)children);
}

static void handle_ExposeWindow(e)
XExposeEvent *e;
{
  CircInfo *cinfo;
  Window w;
  char *label;
  
  w = e->window;
  if (w == top_w) {
    diagram_map_icons();
    diagram_draw_wires(top_w);
    if (manip_type == 0) label = "Choose Parameter to Modify";
    else                 label = "Choose Variable to Plot";
    diagram_draw_label(top_w, label);
  } else {
    if (!XFindContext(dpy, w, circuit_data, (caddr_t *)&cinfo)) {
      if (cinfo && cinfo->mk_fn)
	(cinfo->mk_fn)(w);
      XMapSubwindows(dpy, w);
    }
  }
}

Window button = 0;
static void handle_ButtonPressed(e)
XButtonEvent *e;
{
  Window w, assoc;
  CircInfo *cinfo;

  w = e->window;

  if (w == top_w) {
    goof();
  } else if (w == button) {
    *place_ptr = *type_ptr = -1; /* Fill return values with null choice */
    done = 1;
  } else {
    
    if (!XFindContext(dpy, w, circuit_data, (caddr_t *)&cinfo)) {
      if (cinfo) switch(cinfo->type) {
      case IconWindow:
	if (!XFindContext(dpy, w, assoc_win, (caddr_t *)&assoc)) {
	  XUnmapWindow(dpy, w);
	  XMapRaised(dpy, assoc);
	}
	break;
      case CircuitWindow:
      case DiodeWindow:
	goof(); /*noop...or error feep*/
	break; 
      default:
	if (cinfo)
	  choose_variable(cinfo, manip_type);
	break;
      }
    }
  }
}

static void handle_EnterWindow(e)
XCrossingEvent *e;
{ 
  Window w = e->window;
  if (w == top_w) {
    ; /*noop*/
  } else switch (e->detail) {
    case NotifyAncestor:
    case NotifyNonlinear:
    case NotifyNonlinearVirtual:
      hilite(w);
      break;
    case NotifyInferior:
      /*noop*/
      break;
    }
}

static void handle_LeaveWindow(e)
XCrossingEvent *e;
{ 
  Window w, icon;
  CircInfo *cinfo;
  XWindowAttributes win_attrs;
  w = e->window;
  if (w == top_w) {
    ; /* do nothing */
  } else {
    if (!XFindContext(dpy, w, circuit_data, (caddr_t *)&cinfo)) {
      if (cinfo && cinfo->type == IconWindow) {
	XGetWindowAttributes(dpy, w, &win_attrs);
	if (win_attrs.map_state == IsUnmapped) {
/*TAG ... I think we would rather do nothing here.
	  if (!XFindContext(dpy, w, assoc_win, &icon)) {
	    XUnmapWindow(dpy, icon);
	    XMapRaised(dpy, w);
	  }
*/ ; 
        } else {
	  de_hilite(w);
	}
      } else switch (e->detail) {
      case NotifyNonlinear:
      case NotifyNonlinearVirtual:
      case NotifyAncestor:
	if (cinfo && cinfo->type == CircuitWindow) {
	  if (!XFindContext(dpy, w, assoc_win, (caddr_t *)&icon)) {
	    XUnmapWindow(dpy, w);
	    XMapRaised(dpy, icon);
	  }
	} else {
	  de_hilite(w);
	}
	break;
      case NotifyInferior:
	;/*noop*/
	break;
      }
    }
  }
}

static void hilite(w)
Window w;
{
  CircInfo *cinfo;

  if (!XFindContext(dpy, w, circuit_data, (caddr_t *)&cinfo)) {
    if (cinfo) switch(cinfo->type) {
    case IconWindow:
      if (cinfo->mk_fn) (cinfo->mk_fn)(w);
      XMapSubwindows(dpy, w);
      XFillRectangle(dpy, w, invertGC, 0, 0, cinfo->width,
		     cinfo->height);
/*TAG
      printf("Highlight this window! w = 0x%x\n", w);
*/
      break;
    case ResistorWindow:
    case CapacitorWindow:
    case VarCapacitorWindow:
      /* These are InputOnly windows, so must instead write to parent */
      { Window root_ret, parent, *children;
	unsigned int nchildren;
	int x, y;
	XQueryTree(dpy, w, &root_ret, &parent, &children, &nchildren);
	XFree((char *)children);
	XTranslateCoordinates(dpy, w, parent, 0, 0, &x, &y, &root_ret);
	XFillRectangle(dpy, parent,invertGC, x, y, cinfo->width,cinfo->height);
	break;
      }
    case CircuitWindow:
    case DiodeWindow:
      /* not hilited */
      break;
    }
  }
}

static void de_hilite(w)
Window w;
{
  CircInfo *cinfo;
  hilite(w);
  if (!XFindContext(dpy, w, circuit_data, (caddr_t *)&cinfo)) {
    if (cinfo->mk_fn) (cinfo->mk_fn)(w);
    XMapSubwindows(dpy, w);
  }
}

static void goof()/* on error buttonpress etc. */
{
/*
printf("GOOF!\n");
*/
}

void choose_variable(cinfo, mode) /* used locally and by diagram.c:mk_sys()*/
CircInfo *cinfo;
int mode;
{
  VarData *vd;

  Window w, w2, button[10], button_done, root_return, child_return;
  char *str;
  int i, n, vars[20];
  XEvent xevent;
  XButtonEvent *bevent = (XButtonEvent *) &xevent;
  int done2, x, y, root_x, root_y;
  unsigned long mask;
  unsigned int mask_return;
  static GC textGC = 0;
  XGCValues xgcv;
  static Pixmap savedUnder = (Pixmap) 0;
  XWindowAttributes att;
  XSizeHints hints;
  XSetWindowAttributes attributes;
  XQueryPointer(dpy, root, &root_return, &child_return, &root_x,
		&root_y, &x, &y, &mask_return);
  attributes.override_redirect = True;
  attributes.background_pixel = white_pixel;
  attributes.border_pixel = black_pixel;
  w = XCreateWindow(dpy, root, x, y, 300, 120, 2, CopyFromParent, InputOutput,
		    CopyFromParent, CWOverrideRedirect | CWBorderPixel | 
		    CWBackPixel, &attributes);
  hints.x = x; hints.y = y; hints.width = 300; hints.height = 120;
  hints.flags = USPosition | USSize;
  XSetNormalHints(dpy, w, &hints);
  XSetTransientForHint(dpy, w, Diagram);
  XDefineCursor(dpy, w, left_ptr_cursor);
  n=0;
  if (mode != 0) for (n = 0; cinfo->depvars[n] != ENDVAR; n++)
    vars[n] = cinfo->depvars[n];
  if (mode == 2) vars[n++] = 999;
  if (mode != 1) for (i = n; cinfo->indepvars[n-i] != ENDVAR; n++)
    vars[n] = cinfo->indepvars[n-i];
  vars[n] = ENDVAR;

  if (mode == 0) str = "Modification Choices:";
  else           str = "Plotting Choices:";
  n = 0;
  while (vars[n] != ENDVAR) {
    if (vars[n] == 999) button[n] = (Window)0; /*space holder*/
    else button[n] = XCreateSimpleWindow(dpy,w, 10, 30+15*n, 10, 10, 1, 
					 white_pixel, black_pixel);
    n++;
  }
  button_done = XCreateSimpleWindow(dpy,w, 10, 30+15*n++, 10, 10, 1, 
				    white_pixel, black_pixel);
  XResizeWindow(dpy, w, 300, 30+15*n);

  mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask | ExposureMask;
  XSelectInput(dpy, w, mask);
  XMapSubwindows(dpy, w);
  {
    /* POP-UP! This does a manual save-under, since I can't get the 
     * automatic one to work.
     */
    XSync(dpy, False); /* Save the most up-to-date picture possible. */
    /* Calculate the footprint of this window. */
    XGetWindowAttributes(dpy, w, &att);
    savedUnder = 
      XCreatePixmap(dpy, root, att.width + 2*att.border_width, 
		    att.height + 2*att.border_width, DefaultDepth(dpy,screen));
    XCopyArea(dpy, root, savedUnder, imageGC, att.x, att.y, 
	      att.width+2*att.border_width,att.height+2*att.border_width,0,0);
  }
  XMapRaised(dpy, w);
  done2 = 0;

  xgcv.font = diagram_font->fid;
  xgcv.foreground = black_pixel;
  xgcv.background = white_pixel;
  if (!textGC) textGC = XCreateGC(dpy, root, 
				  GCFont | GCForeground | GCBackground, &xgcv);
  while(!done2) {
    XWindowEvent(dpy, w, mask, &xevent);
    switch ((int) xevent.type) {
    case Expose:
      if (((XExposeEvent *)&xevent)->count == 0) {
	/* Compress multiple rectangle exposure events, using only the last */
	XDrawString(dpy, w, textGC, 10, 10, str, strlen(str));
	for (n=0; vars[n] != ENDVAR; n++) {
	  if (vars[n] == 999) XDrawString(dpy, w, textGC, 30, 40+15*n,
	     "----------------------------------------", 40);
	  else {
	    vd = get_vardata(cinfo->place, vars[n]);
	    XDrawString(dpy, w, textGC, 30, 40+15*n, vd->name, 
			strlen(vd->name));
	  }
	}
	XDrawString(dpy,w,textGC, 30, 40+15*n, "Continue", strlen("Continue"));
      }
    case EnterNotify:
      break;
    case LeaveNotify:
      break;
    case ButtonPress:
      w2 = bevent->window;
      if (bevent->subwindow) w2 = bevent->subwindow;
      if (w2 == w) break;
      if (w2 == button_done) {done2 = 1; break;}
      for (n=0; n<10; n++) if (button[n] == w2) break;
/* IF mode == 0, should make a NumberLine instead! */
      /*return to set up plot with this variable */
      *place_ptr = cinfo->place;
      *type_ptr = vars[n];
      done2 = 1;
      done = 1;
      break;
    }
  }
  /* POP DOWN! */
  XDestroyWindow(dpy, w);
  /* Try to avoid redisplays, since we have saved all the bits. */
  XCopyArea(dpy, savedUnder, root, imageGC, 0, 0,
	    att.width + 2*att.border_width, att.height + 2*att.border_width,
	    att.x, att.y);
  XFreePixmap(dpy, savedUnder); savedUnder = (Pixmap) 0;
  /* calling routine must discard external exposure events. */
  return;
}


diagram_draw_label(w, label)
Window w;
char *label;
{
  static XFontStruct *largeFont = NULL;
  Window root_return;
  int x_return, y_return;
  unsigned int width_return, height_return,
		border_return, depth_return;
  int string_x;
  XGCValues xgcv;

  XGetGeometry(dpy, w, &root_return, &x_return, &y_return,
		&width_return, &height_return, 
  		&border_return, &depth_return);
  if (!largeFont) largeFont = XLoadQueryFont(dpy, LARGE_FONT);
  if (largeFont) {
    xgcv.font = largeFont->fid;
    XChangeGC(dpy, labelGC, GCFont, &xgcv);
    string_x = width_return/2 - XTextWidth(largeFont, label, strlen(label))/2;
  } else
    string_x = width_return/2 - 10;
  XDrawString(dpy, w, labelGC, string_x, 30, label, strlen(label));
}


