#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_end(Widget,XEvent*,char*,int);
NORET rebox_abort(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_end();
NORET rebox_abort();
NORET rebox_move();
NORET rebox_draw_box();
#endif

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

char *ReboxTrans=
  "None <Btn1Down>:rebox_start() \n\
   None <Btn1Motion>:rebox_move() \n\
   <Leave>:rebox_abort() \n\
   None <Btn1Up>:rebox_end()";

static Registry MonitoredWidgets;

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()
{
  MonitoredWidgets=Registry_create(Registry_ptrcmp,
				   Registry_ptrhash);
  XtAddActions(ReboxActions,4);
}

/*--------------------------------------------------*/
NORET rebox_prepare_client(w)
Widget w;
{
  XtAugmentTranslations(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,XtNforeground);

  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;

  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->started==Bool_TRUE)
    Al_fatal_error("rebox: attempt to restart a rebox: fatal in nature\n");
  if (ri->Active==Bool_FALSE)
    return;

  ri->started=Bool_TRUE;
  if (xe->type!=ButtonPress) Al_fatal_error("not button press event\n");
  event=(XButtonEvent*)xe;

  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_end(w,xe,parms,nparms)
Widget w;
XEvent *xe;
char* parms;
int nparms;
{
  RapPack rp;
  ReboxID ri;
  ReboxCallback CB;
  VOIDP cbd;
  XButtonEvent *event;

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

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

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

  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);
  }
  AlUI_DetachCursor(w); 
  ri->Active=Bool_FALSE;
  cbd=RapPack_get(rp,3);
  (CB)(cbd,ri->start_x,ri->start_y,
       ri->end_x,ri->end_y);
}





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

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

  if (ri->Active==Bool_FALSE)  return;
  if (ri->started==Bool_FALSE) return;
  
  ri->started=Bool_FALSE;
  {
    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);
  }
}


/*--------------------------------------------------*/
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);
}


