/* Copyright (C) 1993, 1992 Nathan Sidwell */
/* RCS $Id: makemris.c,v 4.18 1994/01/25 18:33:03 nathan Stable $ */
/*{{{  includes*/
#include "xmris.h"
#include "defmris.h"
#ifndef AMIGAOS
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Paned.h>
#include "makecom.c"
#endif /* AMIGAOS */
/*}}}*/
/*{{{  tables*/
/*{{{  static color*/
static struct
{
  unsigned  backgrounds;      /* normal background */
  unsigned  current[2][3];    /* current colors */
  int       state;            /* type of colors */
  unsigned  busy;             /* not arrived at target */
  unsigned  whizz;            /* whizz value */
  unsigned  count;            /* delay count */
} color;    /* color information */
/*}}}*/
/*{{{  static Arg toplevel_args[] =*/
#ifndef AMIGAOS
static Arg toplevel_args[] =
{
  {XtNinput, True},
};
#endif /* AMIGAOS */
/*}}}*/
/*}}}*/
#ifndef AMIGAOS */
/*{{{  prototypes*/
static Boolean convert_string2keysym PROTOARG((Display *, XrmValue *,
    Cardinal *, XrmValue *, XrmValue *, XtPointer *));
/*}}}*/
/*{{{  Boolean convert_string2keysym(display, args, num_args, from, to, data)*/
static Boolean convert_string2keysym
/* ARGSUSED */
FUNCARG((display, args, num_args, from, to, data),
	Display   *display
ARGSEP  XrmValue  *args
ARGSEP  Cardinal  *num_args
ARGSEP  XrmValue  *from
ARGSEP  XrmValue  *to
ARGSEP  XtPointer *data
)
/*
 * converts a key name string to a key code
 */
{
  static KeySym keysym;
  
  if(to->size < sizeof(KeySym))
    {
      to->size = sizeof(KeySym);
      return False;
    }
  keysym = XStringToKeysym((char CONST *)from->addr);
  if(keysym != NoSymbol)
    {
      to->size = sizeof(KeySym);
      if(to->addr)
	*(KeySym *)to->addr = keysym;
      else
	to->addr = (XtPointer)&keysym;
      return True;
    }
  else
    {
      XtDisplayStringConversionWarning(display, from->addr, XtRKeySym);
      return False;
    }
}
/*}}}*/
/*{{{  void create_widget()*/
extern VOIDFUNC create_widget FUNCARGVOID
/*
 * Create the graphics contexts, sprites and colours and stuff.
 * sets visual and stuff for the toplevel widget
 */
{
  display.form = XtVaCreateManagedWidget("form", formWidgetClass,
      display.toplevel, XtNdefaultDistance, (XtArgVal)0, NULL);
  display.garden = XtVaCreateManagedWidget(&names[data.gender != False][1],
      coreWidgetClass, display.form, XtNwidth, (XtArgVal)WINDOW_WIDTH,
      XtNheight, (XtArgVal)WINDOW_HEIGHT, NULL);
  create_resources(display.garden);
  assert(!(VEL_X_FAST * FAST_STEPS * 2 % VEL_X));
  assert(!(VEL_Y_FAST * FAST_STEPS * 2 % VEL_Y));
  return;
}
/*}}}*/
#endif /* AMIGAOS */
/*{{{  void color_cycle()*/
extern VOIDFUNC color_cycle FUNCARGVOID
/* 
 * Moves the background colors towards their targets. Called during
 * each animation cycle, and programmed by color_set.
 */
{
  if(color.busy)
    {
      assert(display.dynamic);
      if(!color.count--)
	{
	  unsigned  target[3];
	  unsigned  ix;
	  
	  color.count = WHIZZ_CYCLES - 1;
	  color.busy = 0;
	  for(ix = 2; ix--;)
	    {
	      /*{{{  set target*/
	      if(color.state == BACKGROUND_WHIZZ)
		/*{{{  set whizz target*/
		{
		  unsigned  cycle, offset;
		  unsigned  a, b, c;
		
		  color.busy = 1;
		  offset = color.whizz % WHIZZ_STEPS * WHIZZ_SIZE;
		  cycle = color.whizz / WHIZZ_STEPS;
		  if(cycle & 1)
		    a = 0xFFFF - offset, b = 0xFFFF, c = 0;
		  else
		    a = 0xFFFF, b = offset, c = 0;
		  if(cycle / 2 == 1)
		    c = b, b = a, a = 0;
		  else if(cycle / 2 == 2)
		    c = a, a = b, b = 0;
		  target[0] = a;
		  target[1] = b;
		  target[2] = c;
		  if(ix)
		    for(cycle = 3; cycle--;)
		      target[cycle] ^= 0xFFFF;
		}
		/*}}}*/
	      else
		/*{{{  set drone or normal target*/
		{
#ifndef AMIGAOS
		  XColor    *sptr;
		  
		  sptr = &colors[backgrounds[color.state ==
		      BACKGROUND_DRONES ? BACKGROUNDS :
		      color.backgrounds][ix]];
		  target[0] = sptr->red;
		  target[1] = sptr->green;
		  target[2] = sptr->blue;
#else
		  ULONG Pen;
		  struct GS_Color *Color;

		  Pen = backgrounds[color.state == BACKGROUND_DRONES ? BACKGROUNDS : color.backgrounds][ix];
		  for (Color=Sprites->Colors.Colors; Pen!=Color->Pen; Color++)
		    ;
		  target[0] = Color->Red>>16;
		  target[1] = Color->Green>>16;
		  target[2] = Color->Blue>>16;
#endif /* AMIGAOS */
		}
		/*}}}*/
	      /*}}}*/
	      /*{{{  move*/
	      {
		unsigned  gun;
		unsigned  *tptr, *cptr;
		
		for(tptr = target, cptr = color.current[ix], gun = 3;
		    gun--; tptr++, cptr++)
		  if(*tptr > *cptr)
		    {
		      if(*tptr - *cptr <= WHIZZ_SIZE)
			*cptr = *tptr;
		      else
			{
			  *cptr += WHIZZ_SIZE;
			  color.busy = 1;
			}
		    }
		  else if(*tptr < *cptr)
		    {
		      if(*cptr - *tptr <= WHIZZ_SIZE)
			*cptr = *tptr;
		      else
			{
			  *cptr -= WHIZZ_SIZE;
			  color.busy = 1;
			}
		    }
	      }
	      /*}}}*/
	      /*{{{  store*/
	      {
#ifndef AMIGAOS
		XColor    *cptr;
		unsigned  *sptr;
		
		sptr = color.current[ix];
		cptr = &colors[COLOR_DYNAMIC + ix];
		cptr->red = sptr[0];
		cptr->green = sptr[1];
		cptr->blue = sptr[2];
		cptr->flags = DoRed | DoGreen | DoBlue;
		XStoreColor(display.display, display.colormap, cptr);
#else
		unsigned  *sptr;
		struct
		  {
		    UWORD Count;
		    UWORD Pen;
		    UWORD Red1;
		    UWORD Red2;
		    UWORD Green1;
		    UWORD Green2;
		    UWORD Blue1;
		    UWORD Blue2;
		    UWORD Quit;
		  } NewColor;

		sptr = color.current[ix];
		NewColor.Red1=NewColor.Red2=sptr[0];
		NewColor.Green1=NewColor.Green2=sptr[1];
		NewColor.Blue1=NewColor.Blue2=sptr[2];
		NewColor.Count=1;
		NewColor.Pen=ix ? display.DynamicColor2 : display.DynamicColor1;
		NewColor.Quit=0;
		LoadRGB32(&display.Screen->Screen->ViewPort,(ULONG *)&NewColor);
#endif /* AMIGAOS */
	      }
	      /*}}}*/
	    }
	  color.whizz = (color.whizz + 1) % (6 * WHIZZ_STEPS);
	}
    }
  return;
}
/*}}}*/
/*{{{  void color_set(type)*/
extern VOIDFUNC color_set
FUNCARG((type),
	int       type
)
/*
 * Set the board background colours to be what they ought.
 * either one of the BACKGROUND_ specials, or a background index.
 * color_cycle actually sets the color for us.
 */
{
  if(display.dynamic)
    {
      color.count = 0;
      if(type < 0 || type >= BACKGROUNDS)
	color.state = type;
      else
	{
	  color.state = BACKGROUND_NORMAL;
	  color.backgrounds = type;
	}
      color.busy = 1;
    }
#ifdef AMIGAOS
  else
    color.busy=0;
#endif
  return;
}
/*}}}*/
#ifndef AMIGAOS
/*{{{  void open_toolkit(arc, argv)*/
extern VOIDFUNC open_toolkit
FUNCARG((argc, argv),
	int     argc
ARGSEP  String  *argv
)
{
#if !__STDC__
  /*{{{  K&R string fixups*/
  {
    char      *ptr;
    
    ptr = XtMalloc(strlen(XMRISVERSION) + strlen(DATE) +
	strlen(title_text[0].text));
    sprintf(ptr, title_text[0].text, XMRISVERSION, DATE);
    title_text[0].text = ptr;
  }
  /*}}}*/
#endif /* __STDC__ */
  extract_options(&argc, argv, command_options);
  default_gender = GAME_GENDER;
  /*{{{  determine gender of the game*/
  if(*argv)
    {
      size_t    length;
      unsigned  ix;
      char CONST *ptr;
	
      for(ptr = *argv + strlen(*argv); ptr != *argv && *ptr != '/'; ptr--)
	/* EMPTY */;
      if(*ptr == '/')
	ptr++;
      for(length = 0; ptr[length] && ptr[length] != '.'; length++)
	/*  EMPTY */;
      for(ix = 2; ix--;)
	{
	  size_t    other;
	  
	  other = strlen(&names[ix][1]);
	  if(length >= other &&
	      !strncmp(&ptr[length - other], &names[ix][1], other))
	    default_gender = ix;
	}
    }
  /*}}}*/
  if(data.help == False)
    /*{{{  more setup*/
    {
      if(data.nodisplay == False)
	{
	  XrmOptionDescRec CONST *ptr;
    
	  /* as options is an extern of unknown size, I can't use
	   * XtNumber() to get its size, so I've done it this way
	   */
	  for(ptr = options; *(char CONST **)ptr; ptr++)
	    /* EMPTY */;
	  display.toplevel = XtAppInitialize(&display.context, "Xmris",
	      options, (Cardinal)(ptr - options), &argc, argv,
	      (char **)NULL, toplevel_args, XtNumber(toplevel_args));
	}
      if(argc > 1)
	/*{{{  error*/
	{
	  unsigned  ix;
	  
	  data.help = True;
	  data.colors = False;
	  fprintf(stderr, "%s: Unknown option%s:", argv[0],
	      argc > 2 ? "s" : "");
	  for(ix = 1; ix != argc; ix++)
	    fprintf(stderr, "%s %s", ix == 1 ? "" : ",", argv[ix]);
	  fputc('\n', stderr);
	}
	/*}}}*/
      else if(data.nodisplay != False)
	{
	  if(data.scores == False && !data.expire &&
	      !data.remove && !data.format)
	    fatal_error("-nodisplay must be accompanied by -scores, -expire, -remove or -format");
	}
      else
	/*{{{  some more setup*/
	{
	  XtResource CONST *ptr;
	  char      *dir;
	  
	  XtAppSetTypeConverter(display.context, XtRString, XtRKeySym,
	      convert_string2keysym, (XtConvertArgRec *)NULL, 0, XtCacheNone,
	      (void (*)PROTOARG((XtAppContext, XrmValue *, XtPointer,
		  XrmValue *, Cardinal *)))NULL);
	  XtAppSetTypeConverter(display.context, XtRString, XtRGender,
	      convert_string2gender, (XtConvertArgRec *)NULL, 0, XtCacheNone,
	      (void (*)PROTOARG((XtAppContext, XrmValue *, XtPointer,
		  XrmValue *, Cardinal *)))NULL);
	  display.display = XtDisplay(display.toplevel);
	  dir = data.dir;
	  for(ptr = resources; *(char CONST **)ptr; ptr++)
	    /* EMPTY */;
	  XtGetApplicationResources(display.toplevel, (XtPointer)&data,
	      resources, (Cardinal)(ptr - resources), (Arg *)NULL, 0);
	  if(dir)
	    data.dir = dir;
	  gettoplevelresources();
	  XtVaSetValues(display.toplevel,
	      XtNtitle, (XtArgVal)names[data.gender != False],
	      XtNiconName, (XtArgVal)names[data.gender != False],
	      NULL);
	  {
	    XKeyboardState keyboard;
	    
	    XGetKeyboardControl(display.display, &keyboard);
	    display.repeat = keyboard.global_auto_repeat;
	  }
	}
	/*}}}*/
      if(!data.dir)
	data.dir = (char *)resources[0].default_addr;
    }
    /*}}}*/
  return;
}
/*}}}*/
#endif /* AMIGAOS */
