/* echoarea.cc 1.11 95/12/28 23:24:28 */


// xspacewarp by Greg Walker (gow@math.orst.edu)

// This is free software. Non-profit redistribution and/or modification
// is allowed and welcome.


// actions and other functions for handling data entry of shield
// levels, faser/torpedo firing angles, coordinates of sectors
// to leap to, game skill levels, self-destruct
// codes. Implements a primitive terminal input with
// backspacing.


#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <ctype.h>		// isdigit()
#include <stdio.h>		// sscanf()
#include <string.h>		// strcmp()
#include "common.hh"
#include "params.hh"
#include "globals.hh"
#include "space_objects.hh"	// enum Weapon
#include "c_block.hh"
#include "messages.hh"

extern void make_universe(int);
extern void draw_mission(void);

#define MAXINPUT 4		// a minus sign, 3 digits, a NULL

static Block cursor;
static char inputstr[MAXINPUT+1];  // saves kbd input
static int inputlen = 0;	// tracks how many chars deposited in inputstr

void echo(const char *, int);
void clear_echo(void);


// the input() action reads values for angles, sector coors, etc.

void input(Widget w, XEvent *event, String *str, Cardinal *len)
{
  char buf[2];			// hold XLookupString() result
  KeySym ksym;			// not used

  // wrong states for kbd input

  if (gamestate.input == ACTION || gamestate.input == REPLAY ||
      gamestate.visual == MISSION || gamestate.visual == ORIENTATION ||
      gamestate.input == PAUSE)
  {
    return;
  }

  // key events only

  if ((event->type != KeyPress) && (event->type != KeyRelease))
     return;
  if ((inputlen == MAXINPUT))
     return;

  // for sector input, inputstr = 2 1-digit coors & NULL separator in between

  if ((gamestate.input == NEWSECTOR) && (inputlen == 3))
     return;
  if ((gamestate.input == SHIELDLEVEL) &&
      (inputlen == 3)) // 3 digits for percent
     return;
  if (XLookupString((XKeyEvent *)event, buf, 2, &ksym, NULL) != 1)
     return;

  inputstr[inputlen] = buf[0];
  inputlen++;
  buf[1] = '\000';		// NULL terminate
  echo(buf, 1);

  // comma in between coords in echo region and a NULL between coors in inputstr

  if ((gamestate.input == NEWSECTOR) && (inputlen == 1))
  {
    echo(",", 1);
    inputstr[inputlen] = '\000';
    inputlen++;
  }
}


// for unreading data	     

void backspace(Widget w, XEvent *event, String *str, Cardinal *len)
{
  Point point;
  int newcol;

  // wrong states for kbd input

  if (gamestate.input == ACTION || gamestate.input == REPLAY ||
      gamestate.visual == MISSION || gamestate.visual == ORIENTATION ||
      gamestate.input == PAUSE)
  {
    return;
  }

  if (inputlen == 0)		// no input to backspace over
     return;

  // graphically erase echoed data

  if ((gamestate.input == NEWSECTOR) && (inputlen == 2)) // erase comma
  {
    inputlen--;			// backspace over NULL separator
    newcol = cursor.getcol() - 2;
    cursor.setcol(newcol);
    point = cursor.northwest();
    XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC,
		   point.x, point.y, 2*BLOCKW, BLOCKH);
    XFillRectangle(DISPLAY, pixmap, defrv_GC,
		   point.x, point.y, 2*BLOCKW, BLOCKH);
  }
  else
  {
    newcol = cursor.getcol() - 1;
    cursor.setcol(newcol);
    point = cursor.northwest();
    XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC,
		   point.x, point.y, BLOCKW, BLOCKH);
    XFillRectangle(DISPLAY, pixmap, defrv_GC,
		   point.x, point.y, BLOCKW, BLOCKH);
  }

  inputlen--;			// delete data from buffer
}


// this action does a lot. it produces the appropriate response
// when the end-of-input key (normally, "enter" or "return") is
// pressed.

void endinput(Widget w, XEvent *event, String *str, Cardinal *len)
{
  int m, n, row, col, shields, skill;
  double deg;
  const double PI = 3.141592654;

  // wrong states for kbd input

  if (gamestate.input == ACTION || gamestate.input == REPLAY ||
      gamestate.visual == MISSION || gamestate.visual == ORIENTATION ||
      gamestate.input == PAUSE)
  {
    return;
  }

  // extract input from inputstr

  inputstr[inputlen] = '\000';

  // data gets interpreted differently according to the gamestate.input
  // setting. gamestate.input indicates why the data was being entered.

  switch (gamestate.input)
  {
  case CODE:			// user entered a self-destruct code
    if (strcmp(inputstr, SDESTRUCTCODE)) // correct destruct code?
       break;
    endever.selfdestruct();
    return;
    break;
  case FASERANGLE:		// user entered a faser firing angle
    if (sscanf(inputstr, "%lf", &deg) <= 0)
       endever.shoot(FASER, DEFANGLE);
    else
       endever.shoot(FASER, PI * deg / (double)180.0); // convert to radians
    break;
  case NEWSECTOR:	    // user entered coordinates of a sector to leap to
    if (inputlen != 3)		// missing a coordinate
       break;
    m = sscanf(&inputstr[0], "%d", &row);
    n = sscanf(&inputstr[2], "%d", &col);
    if ((m <= 0) || (n <= 0))
       break;
    if ((row < 1) || (row > UROWS) || (col < 1) || (col > UCOLS))
       break;
    endever.leap(row, col);   // gamestate.visual is set within endever.leap()
    if (universe[row-1][col-1].getbhpop()) // warning message if blackhole
    {
      clear_echo();
      echo(bh_warning_str, XtNumber(bh_warning_str));
      gamestate.input = ACTION;	// restore keys: stop data entry keybindings
      return;
    }
    break;
  case SHIELDLEVEL:		// user entered a shield level setting
    m = sscanf(inputstr, "%d", &shields);
    if ((m <= 0) || (shields < 0) || (shields > 100)) // a percent
       break;
    endever.setshields(shields);
    break;
  case SKILL:			// user entered a skill level
    m = sscanf(inputstr, "%d", &skill);
    if ((m <= 0) || (skill < MINSKILL) || (skill > MAXSKILL))
    {
      m = inputlen;
      for (n = 0; n < m; n++)         // try again till get it right
         backspace(NULL, NULL, NULL, NULL);
      return;
    }
    gamestate.skill = skill;
    make_universe(skill);
    clear_echo();		// reset cursor for next use
    draw_mission();
    gamestate.visual = MISSION;
    return;
  case TORPANGLE:		// user entered a torpedo firing angle
    if (sscanf(inputstr, "%lf", &deg) <= 0)
       endever.shoot(TORPEDO, DEFANGLE);
    else
       endever.shoot(TORPEDO, PI * deg / (double)180.0); // convert to radians
    break;
  }

  clear_echo();
  gamestate.input = ACTION;	// restore keys: stop data entry keybindings
}


// draw a string in the echoareas (lower left) of both the
// pixmap and the widget. len = string length w/o the NULL.

void echo(const char *str, int len)
{
  Point point;
  int newcol;

  // echo input string

  point = cursor.origin();
  XDrawString(DISPLAY, XtWindow(widget), def_GC, point.x, point.y, str, len);
  XDrawString(DISPLAY, pixmap, def_GC, point.x, point.y, str, len);

  // update cursor

  newcol = cursor.getcol() + len;
  cursor.setcol(newcol);
}


// initialize the cursor using the geometry info in app_data

void init_echo(void)
{
  cursor.setrow(ECHOROW);
  cursor.setcol(ECHOCOL);
}


// erase the echo area in both pixmap and the widget.

void clear_echo(void)
{
  Point point;
  int width;

  inputlen = 0;			// reset for next use

  if (cursor.getcol() == ECHOCOL)  // nothing in echo area
     return;

  width = BLOCKW*(cursor.getcol() - ECHOCOL);
  cursor.setcol(ECHOCOL);	// reset cursor position
  point = cursor.northwest();
  XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC,
		 point.x, point.y, width, BLOCKH);
  XFillRectangle(DISPLAY, pixmap, defrv_GC,
		 point.x, point.y, width, BLOCKH);
}

// end
