#include "assert.h"   /* must be first for use eith hc2 */
#include "rebox.h"
#include "registry.h"
#include "bool.h"
#include "memory.h"
#include "al.h"
#include "rem.h"
#include "rap.h"
#include "assert.h"
#include "xstuff.h"

#ifdef __STDC__
NORET rebox_start(Widget,XEvent*,char*,int);
NORET rebox_move(Widget,XEvent*,char*,int);
NORET rebox_draw_box(Display*,Drawable,GC,int,int,int,int);
#else
NORET rebox_start();
NORET rebox_move();
NORET rebox_draw_box();
#endif

XtActionsRec ReboxActions[]={
  { "rebox_start", rebox_start },
  { "rebox_move", rebox_move },
};

#define REBOX_ACTION_COUNT 3

char *ReboxTrans=
  "<BtnDown>,<BtnUp>:rebox_start() \n\
   <MouseMoved>:rebox_move()";

static Registry MonitoredWidgets=NULL;

typedef struct ReboxID_str {
  Bool started, Active;
  int start_x, start_y, end_x, end_y;
  int c_x, c_y;
} *ReboxID;

/*--------------------------------------------------*/
NORET rebox_init(App)
XtAppContext App;
{
  if (MonitoredWidgets==NULL)
    MonitoredWidgets=Registry_create(Registry_ptrcmp,
				     Registry_ptrhash);
    
  XtAppAddActions(App,ReboxActions,REBOX_ACTION_COUNT);
}

/*--------------------------------------------------*/
NORET rebox_prepare_client(w)
Widget w;
{
  XtOverrideTranslations(w,
			 XtParseTranslationTable(ReboxTrans));
}


/*--------------------------------------------------*/
NORET rebox_Set_callback_data(w,data)
Widget w;
VOIDP data;
{
  RapPack old, new;
  old=(RapPack)Registry_get(MonitoredWidgets,w);
  if (old==NULL)
    Al_fatal_error("rebox: request to set callback on non added client.\n");
  Registry_remove(MonitoredWidgets,w);
  
  new=RapPack_create4(RapPack_get(old,0),
		      RapPack_get(old,1),
		      RapPack_get(old,2),
		      data);
  RapPack_destroy(old);
  Registry_add(MonitoredWidgets,w,new);
}

/*--------------------------------------------------*/
NORET rebox_add_client(w,CB,cbd)
Widget w;
ReboxCallback CB;
VOIDP cbd;
{
  ReboxID ri;
  XGCValues gcv;
  GC mygc;

  gcv.function=GXxor;
  gcv.subwindow_mode=IncludeInferiors;
  gcv.foreground=(Pixel)Alxt_get_widget_feature(w,XmNbackground);

  mygc=XCreateGC(XtDisplay(w),
		 XtWindow(w),
		 GCFunction | GCSubwindowMode | 
		 GCForeground,
		 &gcv);
  
  assert(mygc);
  if (Registry_get(MonitoredWidgets,w)!=NULL)
    return;  /* add code to add extra callabacks later */
  ri=(ReboxID)Memory_allocate(sizeof(struct ReboxID_str));
  ri->start_x=(-1); ri->end_x=(-1);
  ri->start_y=(-1); ri->end_y=(-1);
  ri->c_x=(-1); ri->c_y=(-1);
  ri->started=Bool_FALSE;
  ri->Active=Bool_FALSE;
  Registry_add(MonitoredWidgets,
	       (VOIDP)w,
	       (VOIDP)RapPack_create4((VOIDP)ri,
				      (VOIDP)CB,
				      (VOIDP)mygc,
				      (VOIDP)cbd));
}

/*--------------------------------------------------*/
NORET rebox_Activate(w)
Widget w;
{
  RapPack rp;
  ReboxID ri;

  if ((rp=(RapPack)Registry_get(MonitoredWidgets,w))==NULL)
    Al_warning("rebox: attempted to activate non registered widget\n");
  ri=(ReboxID)RapPack_get(rp,0);
  ri->Active=Bool_TRUE;
}

/*--------------------------------------------------*/
NORET rebox_start(w,xe,parms,nparms)
     Widget w;
     XEvent *xe;
     char* parms;
     int nparms;
{
  RapPack rp;
  ReboxID ri;
  ReboxCallback CB;
  XButtonEvent *event;
  VOIDP cbd;

  if ((rp=(RapPack)Registry_get(MonitoredWidgets,w))==NULL)
    Al_warning("rebox: attempted to start rebox on unregistered widget\n");
  ri=(ReboxID)RapPack_get(rp,0);
  CB=(ReboxCallback)RapPack_get(rp,1);

  if (ri->Active==Bool_FALSE)
    return;

  if (xe->type!=ButtonRelease) 
    Al_warning("not button press event on end\n");
  event=(XButtonEvent*)xe;

  if (event->button!=Button1) {
    ri->started=Bool_FALSE;
    ri->Active=Bool_FALSE;
    
    XUngrabPointer(XtDisplay(w),CurrentTime);
    AlUI_DetachCursor(w); 
    
    {
      GC gc;
      Display *dis=XtDisplay(w);
      Drawable draw=XtWindow(w);
      gc=(GC)RapPack_get(rp,2);
      rebox_draw_box(dis,draw,gc,
		     ri->start_x,ri->start_y,
		     ri->c_x,ri->c_y);
    }
    return;
  }
  
  if (ri->started==Bool_TRUE) {
    ri->started=Bool_FALSE;
    ri->Active=Bool_FALSE;
    RE_debug("REBOX END\n");

    XUngrabPointer(XtDisplay(w),CurrentTime);
    AlUI_DetachCursor(w); 

    ri->end_x=event->x;
    ri->end_y=event->y;
    RE_debug1("press x=%d  ",ri->end_x);
    RE_debug1("press y=%d\n",ri->end_y);
    {
      GC gc;
      Display *dis=XtDisplay(w);
      Drawable draw=XtWindow(w);
      gc=(GC)RapPack_get(rp,2);
      rebox_draw_box(dis,draw,gc,
		     ri->start_x,ri->start_y,
		     ri->c_x,ri->c_y);
    }
    cbd=RapPack_get(rp,3);
    (CB)(cbd,ri->start_x,ri->start_y,
	 ri->end_x,ri->end_y);
  }
  else {
    ri->started=Bool_TRUE;
    if (xe->type!=ButtonRelease) Al_fatal_error("not button release event\n");
    event=(XButtonEvent*)xe;
    RE_debug("REBOX START\n");

    XGrabPointer(XtDisplay(w),
		 XtWindow(w),
		 FALSE,         /* don't report events to owners, just to me */
		 PointerMotionMask |
		 ButtonPressMask |
		 ButtonReleaseMask,  /* report pointer motion events */
		 GrabModeAsync,    /* pointer grab mode */
		 GrabModeAsync,    /* keyboard grab mode */
		 XtWindow(w),      /* confine to */
		 None,             /* no cursor */
		 CurrentTime);    
    
    ri->start_x=event->x;
    ri->start_y=event->y;
    ri->c_x=ri->start_x;
    ri->c_y=ri->start_y;
    AlUI_AttachCursor(w,BOXER_RIGHT_CURSOR); 
  }
}

/*--------------------------------------------------*/
NORET rebox_post_realize_client(w)
     Widget w;
{
}


/*--------------------------------------------------*/
NORET rebox_move(w,xe,parms,nparms)
     Widget w;
     XEvent *xe;
     char* parms;
     int nparms;
{
  RapPack rp;
  ReboxID ri;
  ReboxCallback CB;
  XMotionEvent *event;
  GC gc;
  Display *dis=XtDisplay(w);
  Drawable draw=XtWindow(w);
  int ex, ey;
  
  if ((rp=(RapPack)Registry_get(MonitoredWidgets,w))==NULL)
    Al_warning("rebox: attempted to move rebox on unregistered widget\n");
  ri=(ReboxID)RapPack_get(rp,0);
  CB=(ReboxCallback)RapPack_get(rp,1);
  gc=(GC)RapPack_get(rp,2);
  if (ri->started==Bool_FALSE) return;
  if (ri->Active==Bool_FALSE)  return;

  event=(XMotionEvent*)xe;
  ex=event->x; ey=event->y;

  rebox_draw_box(dis,draw,gc,
		 ri->start_x,ri->start_y,
		 ri->c_x,ri->c_y);
  rebox_draw_box(dis,draw,gc,
		 ri->start_x,ri->start_y,
		 ex,ey);

  ri->c_x=ex;
  ri->c_y=ey;
}


/*--------------------------------------------------*/
NORET rebox_draw_box(dis,draw,gc,lx,ly,rx,ry)
Display *dis;
Drawable draw;
GC gc;
int lx,ly,rx,ry;
{
  int sx,sy,fx,fy,w,h,x1,x2,y1,y2;
  if (ly>ry || lx>rx) {
    sx=rx; sy=ry;
    fx=lx; fy=ly;
  }
  else {
    fx=rx; fy=ry;
    sx=lx; sy=ly;
  }
  w=fx-sx;
  h=fy-sy;
  XDrawRectangle(dis,draw,gc,sx,sy,w,h);
  x1=(int)w/3; x2=x1+x1;
  y1=(int)h/3; y2=y1+y1;
  XDrawLine(dis,draw,gc,
	    sx+x1, sy,
	    sx+x1, fy);
  XDrawLine(dis,draw,gc,
	    sx+x2, sy,
	    sx+x2, fy);    
  XDrawLine(dis,draw,gc,
	    sx, sy+y1,
	    fx, sy+y1);
  XDrawLine(dis,draw,gc,
	    sx, sy+y2,
	    fx, sy+y2);
}


