/*LINTLIBRARY*/

/*  @(#)tty.c 1.2 90/04/09
 *
 *  These are the dumb tty dependent graphics routines used by othello.
 *
 *  Copyright (c) - Rich Burridge - Sun Microsystems.
 *                                  All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  introductory messages are not removed, and no monies are exchanged.
 *
 *  No responsibility is taken for any errors or inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include "othello.h"
#include "color.h"
#include "extern.h"

/* NOTE: This is a classic example of reverse engineering. These routines
 *       have been modified (read hacked), to use the same interface as
 *       the pixel graphics ones. Therefore they have to do some strange
 *       checking to make sure everything comes out correctly.
 */

char *CE, *CL, *CM, *SE, *SO ;

char *getenv(), *tgetstr(), *tgoto() ;
int destroy_frame(), outc() ;

struct sgttyb in_new, in_old ;

/* Information for all the panel items. */

struct tty_info {     /* Information for all the panel items. */
  int column ;        /* Column number. */
  int row ;           /* Row number. */
  int x ;             /* Pseudo x mouse position. */
  int y ;             /* Pseudo y mouse position. */
} ;

struct tty_info ttyvals[MAXITEMS] = {
      { -1, -1,  15, 20, },      /* Done (not displayed). */
      { 11,  1,  60, 20, },      /* Last */
      { 22,  1, 105, 20, },      /* New game */
      { 33,  1, 220, 20, },      /* Suggest */
      { 44,  1, 280, 20, },      /* Undo */
      { 55,  1, 175, 20, },      /* Quit */
      { 38,  9,  15, 65, },      /* Computer plays:  */
      { 38, 11, 200, 65, },      /* Remarks:  */
      { 38,  5,  15, 45, },      /* Aspiration:  */
      { 38,  7, 200, 45, },      /* Difficulty:  */
      { -1, -1,  -1, -1, },      /* Panel message. */
      { -1, -1,  -1, -1, },      /* Remark message. */
      { -1, -1,  -1, -1, },      /* Score message. */
} ;

struct other_info {   /* Information needed to place other text values. */
  int column ;        /* Column number. */
  int row ;           /* Row number. */
} ;

struct other_info othervals[MAXITEMS] = {
      { -1,  -1, },      /* Done     (ignored). */
      { -1,  -1, },      /* Last     (ignored). */
      { -1,  -1, },      /* New game (ignored). */
      { -1,  -1, },      /* Suggest  (ignored). */
      { -1,  -1, },      /* Undo     (ignored). */
      { -1,  -1, },      /* Quit     (ignored). */
      { 60,   9, },      /* Computer plays:  */
      { 60,  11, },      /* Remarks:  */
      { 60,   5, },      /* Aspiration:  */
      { 60,   7, },      /* Difficulty:  */
      { 38,  15, },      /* Panel message. */
      { 38,  17, },      /* Remark message. */
      { 38,  19, },      /* Score message. */
} ;


SIGRET
cleanup()
{
  destroy_frame() ;
}


/*ARGSUSED*/
batch(state)             /* Graphics batching - null routine. */
enum bltype state ;
{}


close_frame()           /* This option does nothing with termcap. */
{}


/*ARGSUSED*/
color_area(x, y, width, height, color)
int x, y, width, height, color ;
{
  char nextline[MAXLINE] ;
  int i ;

/*  There are two cases we care about here:
 *
 *  (1) If the width is TOTAL_WIDTH and the height is TOTAL_HEIGHT, then
 *      we clear the whole of the screen. We also draw the board at this
 *      time. With the pixel based graphics versions, this is normally
 *      done by low-level graphics routines, but to fully emulate that is
 *      just too hard.
 *
 *  (2) When we are "undo"ing a piece. A check is made to if the width and
 *      the height of the area we are coloring is PSIZE. If so, then we
 *      display a space at the appropriately adjusted row/column.
 */

  if (width == TOTAL_WIDTH && height == TOTAL_HEIGHT)
    {
      tputs(CL, 1, outc) ;
      tc_move(4, 3) ;
      outstr("a   b   c   d   e   f   g   h") ;
      for (y = 4; y < 21; y++)
        {
          tc_move(1, y) ;
          if (y % 2)
            SPRINTF(nextline, "%c|   |   |   |   |   |   |   |   |%c",
                              '1' + (y - 4) / 2, '1' + (y - 4) / 2) ;
          else       SPRINTF(nextline, " +---+---+---+---+---+---+---+---+") ;
          outstr(nextline) ;
        }
      tc_move(4, 21) ;
      outstr("a   b   c   d   e   f   g   h") ;
    }
  else if (width == PSIZE && height == PSIZE)
    {
      tc_move(4 + 4 * (((x - BBORDER - PIECE_MARGIN) / CELL_SIZE) & 7),
              5 + 2 * ((y - CY - BBORDER - PIECE_MARGIN) / CELL_SIZE)) ;
      outc(' ') ;
    }
}


/*ARGSUSED*/
create_menu(mtype, values)     /* Create popup menus - null routine. */
enum panel_type mtype ;
char *values[] ;
{}


destroy_frame()       /* Destroy Othello window. */
{
  int i ;

  tputs(CL, 1, outc) ;
  SIGNAL(SIGINT, SIG_IGN) ;
  IOCTL(0, TIOCSETP, &in_old) ;
  exit(0) ;
}


do_clr_eol()                  /* Clear to the end of the line. */
{
  tputs(CE, 1, outc) ;
}


/*ARGSUSED*/
do_menu(mtype)                /* Popup appropriate menu - not used. */
enum panel_type mtype ;
{
  return 0 ;
}


do_standend()                 /* Finish inverted area. */
{
  tputs(SE, 1, outc) ;
}


do_standout()                 /* Start inverted area. */
{
  tputs(SO, 1, outc) ;
}


/*ARGSUSED*/
draw_image(x, y, width, height, image)    /* Null routine. */
int x, y, width, height ;
enum image_type image ;
{}


/*ARGSUSED*/
draw_line(x1, y1, x2, y2, op, color)
int x1, y1, x2, y2, color ;
enum optype op ;
{
  char ch ;

/*  There is just one case we care about here; the "suggest" cross. To
 *  determine if this is such a case, we check if the two x values differ
 *  by 10, and the two y values differ by 10. If so, then a plus sign is
 *  output at the appropriate row/column, if the color is black, otherwise
 *  a space is output.
 */

  if (abs(x1 - x2) == 10 && abs(y1 - y2) == 10)
    {
      ch = '+' ;
      tc_move(4 + 4 * (((x1 - BBORDER - PIECE_MARGIN) / CELL_SIZE) & 7),
              5 + 2 * ((y1 - CY - BBORDER - PIECE_MARGIN) / CELL_SIZE)) ;
      if (color != C_BLACK) ch = ' ' ;
      outc(ch) ;
    }
}


/*ARGSUSED*/
draw_stencil(x, y, width, height, op, color, stencil, image)
int x, y, width, height, color ;
enum optype op ;
enum image_type stencil, image ;
{
  int ch ;

       if (image == P_BLACK) ch = 'X' ;
  else if (image == P_WHITE) ch = 'O' ;
  else return ;
       if (op == RCLR) ch = ' ' ;
  else if (op == RINV) return ;

  tc_move(4 + 4 * (((x - BBORDER - PIECE_MARGIN) / CELL_SIZE) & 7),
          5 + 2 * ((y - CY - BBORDER - PIECE_MARGIN) / CELL_SIZE)) ;
  outc(ch) ;
}


draw_string(x, y, str, flag)    /* Display str at x,y. */
int x,y, flag ;
char *str ;
{

/*  If flag is set, then we clear to the end of line, otherwise we
 *  display the text str in reverse video.
 */

  if (flag)
    {
      tc_move(x, y) ;
      do_clr_eol() ;
    }
  else do_standout() ;

  tc_move(x, y) ;
  outstr(str) ;
  if (!flag) do_standend() ;
}


/*  Text can be one of three things:
 *
 *  (1) Text for "panel" items.
 *  (2) Text for "cyclic" items.
 *  (3) Other text.
 *
 *  First we check to see if this is one of the panel message strings.
 *
 *  Next we check if this is a panel button or cycle types.
 *
 *  For all other text, we check where it is being displayed against
 *  a list of possible values. This is then remapped to the correct
 *  row/column number, and displayed.
 */

/*ARGSUSED*/
draw_text(x, y, ftype, color, str)
enum font_type ftype ;
int x, y, color ;
char *str ;
{
  enum panel_type pt ;
  int black, white ;
  int i ;

/* Check for the three panel message types. */

  if (y == 153)                                         /* Panel message. */
    {
      if (EQUAL(str, "To move"))
        STRCPY(str, "Select a letter/number pair for a move") ;
      draw_string(othervals[(int) PANEL_MES].column,
                othervals[(int) PANEL_MES].row,  str, TRUE) ;
    }
  else if (y == 195)                                    /* Remarks message. */
    draw_string(othervals[(int) REMARK_MES].column,
                othervals[(int) REMARK_MES].row, str, TRUE) ;
  else if (y == 237)                                    /* Score message. */
    {
      if (sscanf(str, "White: %d, Black: %d", &white, &black) == 2)
        SPRINTF(str, "White (O): %d, Black (X): %d", white, black) ;
      draw_string(othervals[(int) SCORE_MES].column,
                  othervals[(int) SCORE_MES].row,  str, TRUE) ;
    }

/* Check for panel and cyclic items. */

  for (i = 0; i < MAXITEMS-3; i++)
    if (EQUAL(str, items[i].text))
      if (ttyvals[i].column != -1)
        {
          draw_string(ttyvals[i].column, ttyvals[i].row, str, FALSE) ;
          return ;
        }
      else return ;

/* Other text. */

  switch (y)
    {
      case 69  : if (x < 250) pt = ASPIRATION ;        /* Aspiration. */
                 else pt = DIFFICULTY ;                /* Difficulty. */
                 break ;
      case 111 : if (x < 250) pt = COMPUTER_PLAYS ;    /* Computer plays. */
                 else pt = REMARK ;                    /* Remarks. */
                 break ;
      default  : return ;    /* We're not interested in this piece of text. */
    }
  draw_string(othervals[(int) pt].column, othervals[(int) pt].row,
              str, TRUE) ;
}
 
 
get_event()            /* Only events possible are keyboard ones. */
{
  char c ;

  tc_move(0, 0) ;      /* Get that bloody cursor out of the way. */
  READ(0, &c, 1) ;
  cur_ch = c & 0177 ;
}
 
 
/*ARGSUSED*/
get_strwidth(ftype, str)    /* Get width in pixels of string value. */
enum font_type ftype ;
char *str ;
{
  return(8) ;
}


init_fonts()             /* Open normal and bold fonts. */
{
  nfont_height = 12 ;    /* Fake the dimensions. */
  bfont_height = 12 ;
}


init_ws_type()
{
  char bp[1024], termtype[MAXLINE] ;
  int i ;
  static char buf[100] ;
  char *area = buf ;

  if (getenv("TERM") != NULL) STRCPY(termtype, getenv("TERM")) ;
  if (tgetent(bp, termtype) != 1) return 1 ;
  if ((CL = tgetstr("cl", &area)) == (char *) 0) return 1 ;
  if ((CM = tgetstr("cm", &area)) == (char *) 0) return 1 ;
  if ((CE = tgetstr("ce", &area)) == (char *) 0) return 1 ;
  if ((SO = tgetstr("so", &area)) == (char *) 0) return 1 ;
  if ((SE = tgetstr("se", &area)) == (char *) 0) return 1 ;
  move_delta = 5 ;
  return 0 ;
}


load_colors()     /* Create and load Othello color map. */
{
  iscolor = 0 ;   /* No colors in the termcap implementation. */
}


/*ARGSUSED*/
lock_screen(state)     /* Graphics locking - null routine. */
enum bltype state ;
{}


make_canvas()               /* Null routine, see the make_frame routine. */
{}


/*ARGSUSED*/
make_frame(argc, argv)      /* Create othello window/icon. */
int argc ;
char *argv[] ;
{
  SIGNAL(SIGINT, cleanup) ;
  IOCTL(0, TIOCGETP, &in_old) ;        /* Setup standard input. */
  in_new = in_old ;
  in_new.sg_flags |= RAW ;
  in_new.sg_flags &= ~(ECHO | CRMOD) ;
  IOCTL(0, TIOCSETP, &in_new) ;
  setbuf(stdout, (char *) NULL) ;
}


make_icon()             /* Null routine - no icon in termcap version. */
{}


outc(c)                 /* Output the next character to the screen. */
int c ;
{
  PUTC(c, stdout) ;
}


outstr(str)
char *str ;
{
  int i ;

  for (i = 0; i < strlen(str); i++) PUTC(str[i], stdout) ;
}


process_event()           /* Process the next user input. */
{
  nextc = KEYBOARD ;
}


/*ARGSUSED*/
set_cursor(cursor)        /* No cursors in termcap version. */
enum curtype cursor ;
{}


start_tool()                 /* Display screen and start event dispatcher. */
{
  init_canvas() ;            /* Equivalent of a FRAME_REPAINT. */

  for (;;)
    {
      get_event() ;          /* Get next canvas event. */
      process_event() ;      /* Find out what kind it is. */
      handle_event() ;       /* And do the apropriate action. */
    }
}


tc_move(x, y)                 /* Move to character position (x, y). */
int x, y ;
{
  tputs(tgoto(CM, x, y), 1, outc) ;
}
