#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "ShuttlebarP.h"

static char defaultTranslations[] = 
  "<Btn1Down>:		Position() Play()\n\
   <Btn1Motion>:	Move() Play()\n\
   <Btn1Up>:		Release() Play()";

/* resource table */
#define offset(field) XtOffset(ShuttleWidget, shuttle.field)

static XtResource resources[] = {
  {XtNinitialSpeed, XtCInitialSpeed, XtRInt, sizeof(int),
     offset(initial_speed), XtRString, "0"},
  {XtNmaxSpeed, XtCMaxSpeed, XtRInt, sizeof(int),
     offset(max_speed), XtRString, "3000"},
  {XtNcenterColor, XtCCenterColor, XtRPixel, sizeof(Pixel),
     offset(center_color), XtRString, "XtDefaultBackground"},
  {XtNforegroundColor, XtCForegroundColor, XtRPixel, sizeof(Pixel),
     offset(foreground_color), XtRString, "XtDefaultForeground"},
  {XtNcenterWidth, XtCCenterWidth, XtRInt, sizeof(int),
     offset(center_width), XtRString, "50"},
  {XtNrollCallback, XtCRollCallback, XtRCallback, sizeof (caddr_t),
     offset(roll), XtRCallback, NULL}
};

/* function predeclarations */
static void DrawPolygon(), DrawRectangle(), InitializeGCs(), FreeGCs();
static int PositionToSpeed(), SpeedToPosition();
static void Resize(), Redisplay(), Initialize();
static Boolean SetValues();
static XtGeometryResult QueryGeometry();

/* Action routines */
static void ShutPosition(), ShutMove(), ShutRelease(), ShutPlay();

static XtActionsRec actions[] = {
  {"Position",	ShutPosition},
  {"Move",	ShutMove},
  {"Release",	ShutRelease},
  {"Play",	ShutPlay},
  {NULL, NULL}
};

/* class definition */
ShuttleClassRec shuttleClassRec = {
  {
#define superclass		(&simpleClassRec)
    /* superclass		*/	(WidgetClass) superclass,
    /* class_name		*/	"Shuttle",
    /* widget_size		*/	sizeof(ShuttleRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	XtInheritRealize,
    /* actions			*/	actions,
    /* num_actions		*/	XtNumber(actions),
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	FreeGCs,
    /* resize			*/	Resize,
    /* expose			*/	Redisplay,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	defaultTranslations,
    /* query_geometry		*/	QueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator, 
    /* extension		*/	NULL 
    }
};

WidgetClass shuttleWidgetClass = (WidgetClass) &shuttleClassRec;


/* Private procedures */

static void Initialize (req, new)
     Widget req, new;

{
  Region region;
  ShuttleWidget shut = (ShuttleWidget) new;

  InitializeGCs (new);
  shut->shuttle.speed = shut->shuttle.return_speed =
    shut->shuttle.initial_speed;

  shut->shuttle.mode = 0;

  forward_shuttle[1].y += shut->core.height;
  forward_shuttle[2].y -= shut->core.height/2;
  forward_shuttle[3].y -= shut->core.height/2;
  reverse_shuttle[1].y += shut->core.height;
  reverse_shuttle[2].y -= shut->core.height/2;
  reverse_shuttle[3].y -= shut->core.height/2;
}

static void Redisplay (w, evt, region)
     Widget w;
     XEvent *evt;
     Region region;
{
  ShuttleWidget shut = (ShuttleWidget) w;
  
  XClearWindow (XtDisplay(w), XtWindow(w));
  XFillRectangle (XtDisplay(w), XtWindow(w), shut->shuttle.centEraseGC,
		  shut->core.width/2 - shut->shuttle.center_width, 0,
		  shut->shuttle.center_width*2, shut->core.height);

  if (!shut->shuttle.mode) DrawRectangle (w,1);
  else DrawPolygon (w,1);
}

static void Resize (w)
     Widget w;

{
}

static Boolean SetValues (current, request, new)
     Widget current, request, new;

{
  Boolean redisplay = FALSE;
  ShuttleWidget curshut = (ShuttleWidget) current;
  ShuttleWidget newshut = (ShuttleWidget) new;

  if (curshut->shuttle.center_color != newshut->shuttle.center_color) {
    InitializeGCs (new);
    redisplay = TRUE;
  }

  if (curshut->shuttle.center_width != newshut->shuttle.center_width) {
    redisplay = TRUE;
  }

  return (redisplay);
}

static XtGeometryResult QueryGeometry (w, intended, preferred)
     Widget w;
     XtWidgetGeometry *intended, *preferred;

{
  return (XtGeometryYes);
}

/* action routines */

static void ShutPosition (w, evt, parms, num)
     Widget w;
     XEvent *evt;
     String *parms;
     Cardinal *num;


{
  ShuttleWidget shut = (ShuttleWidget) w;
  int mode,
  	speed = PositionToSpeed (evt->xbutton.x, shut, &mode);

  if (shut->shuttle.speed == 0) DrawRectangle (w,0);
  else DrawPolygon (w,0);

  /* printf ("Shuttle speed: %d [%d]\n",speed,mode); */

  shut->shuttle.mode = mode;
  shut->shuttle.return_speed = shut->shuttle.speed;
  shut->shuttle.speed = speed;
  
  if (!shut->shuttle.speed) DrawRectangle (w,1);
  else DrawPolygon (w,1);

}

static void ShutRelease (w, evt, parms, num)
     Widget w;
     XEvent *evt;
     String *parms;
     Cardinal *num;

{
  ShuttleWidget shut = (ShuttleWidget) w;

  if (!shut->shuttle.speed) DrawRectangle (w,0);
  else DrawPolygon (w,0);

  shut->shuttle.mode = 0;
  shut->shuttle.speed = shut->shuttle.return_speed;
  
  if (!shut->shuttle.speed) DrawRectangle (w,1);
  else DrawPolygon (w,1);
}

static void ShutMove (w, evt, parms, num)
     Widget w;
     XEvent *evt;
     String *parms;
     Cardinal *num;

{
  ShuttleWidget shut = (ShuttleWidget) w;
  int mode,
  	speed = PositionToSpeed (evt->xbutton.x, shut, &mode);

  if (shut->shuttle.speed == 0) DrawRectangle (w,0);
  else DrawPolygon (w,0);

  /* printf ("Shuttle speed: %d [%d]\n",speed,mode); */

  shut->shuttle.mode = mode;
  shut->shuttle.speed = speed;
  
  if (!shut->shuttle.speed) DrawRectangle (w,1);
  else DrawPolygon (w,1);
}

static void ShutPlay (w, evt, parms, num)
     Widget w;
     XEvent *evt;
     String *parms;
     Cardinal *num;

{
  ShuttleWidget shut = (ShuttleWidget) w;
  XtCallCallbacks (w, XtNrollCallback, (caddr_t) shut->shuttle.speed);
}


/* external functions */

int XtShutGetSpeed (w)
     Widget w;

{
  ShuttleWidget shut;
  if (!XtIsSubclass (w, shuttleWidgetClass)) return 0;
  
  shut = (ShuttleWidget) w;
  return (shut->shuttle.speed);
}

int XtShutSetSpeed (w, speed)
     Widget w;
     int speed;

{
  ShuttleWidget shut;
  if (!XtIsSubclass (w, shuttleWidgetClass)) return 0;
  
  shut = (ShuttleWidget) w;
  shut->shuttle.speed = speed;
  Redisplay (w, NULL, NULL);
  return (speed);
}

/* internal functions */

static int PositionToSpeed (pos, shut, mode)
     int pos;
     int *mode;
     ShuttleWidget shut;

{
  int speed;

  *mode = 0;
  if (pos == shut->core.width/2) return (0);

  if (pos < shut->core.width/2 - shut->shuttle.center_width)
    return -((shut->core.width/2 - shut->shuttle.center_width - pos) * 
	     shut->shuttle.max_speed /
	     (shut->core.width/2 - shut->shuttle.center_width));

  if (pos > shut->core.width/2 + shut->shuttle.center_width)
    return ((pos - shut->core.width/2 - shut->shuttle.center_width) * 
	     shut->shuttle.max_speed /
	     (shut->core.width/2 - shut->shuttle.center_width));

  *mode = 1;

  if (pos > shut->core.width/2)
    return 15;
 
  *mode = -1;
  return (-15);

}

static int SpeedToPosition (shut)
     ShuttleWidget shut;

{
  int pos,
      spd = shut->shuttle.speed, 
      mode = shut->shuttle.mode;

  if (mode != 0) {
    /* printf ("center... %d\n",mode); */
    return (spd+shut->core.width/2);
  }

  pos = (spd * (int) (shut->core.width/2 - shut->shuttle.center_width) /
    (int) shut->shuttle.max_speed) + (int) shut->core.width/2;
  
  /* printf ("%d -- %d\n",pos,spd); */
  if (spd < 0) pos -= shut->shuttle.center_width;
  if (spd > 0) pos += shut->shuttle.center_width;

  return (pos);
}

static void InitializeGCs (w)
     Widget w;

{
  ShuttleWidget shut = (ShuttleWidget) w;
  XGCValues val;
  unsigned long mask = GCForeground|GCBackground;

  val.foreground = shut->shuttle.foreground_color;
  val.background = shut->core.background_pixel;
  shut->shuttle.drawGC = XtGetGC (w, mask, &val);

  val.foreground = val.background = shut->core.background_pixel;
  shut->shuttle.eraseGC = XtGetGC (w, mask, &val);

  val.foreground = shut->shuttle.foreground_color;
  val.background = shut->shuttle.center_color;
  shut->shuttle.centDrawGC = XtGetGC (w, mask, &val);

  val.foreground = val.background = shut->shuttle.center_color;
  shut->shuttle.centEraseGC = XtGetGC (w, mask, &val);
}

static void FreeGCs (w)
     Widget w;

{
  ShuttleWidget shut = (ShuttleWidget) w;

  XFreeGC (XtDisplay(w), shut->shuttle.drawGC);
  XFreeGC (XtDisplay(w), shut->shuttle.eraseGC);
  XFreeGC (XtDisplay(w), shut->shuttle.centDrawGC);
  XFreeGC (XtDisplay(w), shut->shuttle.centEraseGC);
}

static void DrawRectangle (w, onoff)
     Widget w;
     Boolean onoff;

{
  ShuttleWidget shut = (ShuttleWidget) w;
  GC temp;
  int spd = SpeedToPosition(shut);

  if (onoff) temp = shut->shuttle.centDrawGC;
  else temp = shut->shuttle.centEraseGC;

  /* printf ("Position; %d[%d]\n",spd,shut->shuttle.mode); */
  XFillRectangle (XtDisplay(w), XtWindow(w), temp, spd - 2,
		  0, 4, shut->core.height);
}

static void DrawPolygon (w,onoff)
     Widget w;
     Boolean onoff;

{
  ShuttleWidget shut = (ShuttleWidget) w;
  GC temp;
  XPoint *shuttle;
  int spd;

  if (onoff) temp = shut->shuttle.drawGC;
  else temp = shut->shuttle.eraseGC;

  if (shut->shuttle.mode != 0) 
    if (onoff) temp = shut->shuttle.centDrawGC;
    else temp = shut->shuttle.centEraseGC;


  if (shut->shuttle.speed > 0) shuttle = forward_shuttle;
  else shuttle = reverse_shuttle;

  spd = SpeedToPosition(shut);
  shuttle[0].x = spd;

  XDrawLines (XtDisplay(w), XtWindow(w), temp, shuttle, 4,
		CoordModePrevious);

  /* printf ("Position: %d %d\n",onoff,spd); */
  XFillRectangle (XtDisplay (w), XtWindow(w), temp, spd-2, 0, 4,
		  shut->core.height);
}
