/****************************************************************/
/*								*/
/*	ic_output.c						*/
/*								*/
/*		Output routines for the "Integer Calculator".	*/
/*								*/
/****************************************************************/
/*   origination        1988-Apr-6          Terrence W. Holm	*/
/****************************************************************/

#include <stdlib.h>
#include <termcap.h>
#include <stdio.h>
#include "ic.h"

/****************************************************************/
/*   		Code for handling termcap			*/
/****************************************************************/
#define  TC_BUFFER  1024	/* Size of termcap(3) buffer	 */
#define  TC_STRINGS  200	/* Enough room for cm,cl,so,se	 */

static char *Tmove;		/* (cm) - Format for tgoto	 */
static char *Tclr_all;		/* (cl) - String to clear screen */
static char *Treverse;		/* (so) - String to reverse mode */
static char *Tnormal;		/* (se) - String to undo reverse */

/****************************************************************/
/*								*/
/*	Init_Termcap()						*/
/*								*/
/*		Initializes the external variables for the	*/
/*		current terminal.				*/
/*								*/
/*		NULL is returned on error conditions.		*/
/*								*/
/****************************************************************/
int Init_Termcap()
{
  char *term;
  char buffer[TC_BUFFER];
  static char strings[TC_STRINGS];
  char *s = &strings[0];

  term = getenv("TERM");
  if (term == NULL) return(0);
  if (tgetent(buffer, term) != 1) return(0);
  if ((Tmove = tgetstr("cm", &s)) == NULL) return(0);
  if ((Tclr_all = tgetstr("cl", &s)) == NULL) return(0);
  if ((Treverse = tgetstr("so", &s)) == NULL) {
	Treverse = s;
	*s = '\0';
	++s;
  }
  if ((Tnormal = tgetstr("se", &s)) == NULL) {
	Tnormal = s;
	*s = '\0';
	++s;
  }
  return(EOF);
}

/****************************************************************/
/*								*/
/*	Move( column, line )					*/
/*								*/
/*		Use the termcap string to move the cursor.	*/
/*								*/
/****************************************************************/
void Move(column, line)
int column;
int line;
{
  Puts(tgoto(Tmove, column, line));
}

/****************************************************************/
/*								*/
/*	Puts( string )						*/
/*								*/
/*		Write the given termcap string to the standard	*/
/*		output device.					*/
/*								*/
/****************************************************************/
void Puts(string)
char *string;
{
  tputs(string, 1, Putchar);
}

void Putchar(c)
char c;
{
  putchar(c);
}

/****************************************************************/
/*   		       Output routines				*/
/****************************************************************/
/****************************************************************/
/*								*/
/*	Draw_Help_Screen()					*/
/*								*/
/****************************************************************/
void Draw_Help_Screen()
{
  Puts(Tclr_all);		/* Clear the screen  */

  printf("\n\n                             ");
  Puts(Treverse);
  printf("IC  COMMANDS");
  Puts(Tnormal);
  printf("\n\n\n");

  printf("         h   Help                            ENTER Push stack\n");
  printf("         i   Input base  (h d o b)            DEL  Clear entry\n");
  printf("  PGDN   l   Last top of stack\n");
  printf("         m   Minix shell                       .   Change sign\n");
  printf("         o   Output base (h d o b a)           +   Add\n");
  printf("  DOWN   p   Pop stack                         -   Subtract\n");
  printf("  END    q   Quit                              *   Multiply\n");
  printf("  LEFT   r   Recall (0-9)                      /   Divide\n");
  printf("  RIGHT  s   Store [+] (0-9)                   %%   Remainder\n");
  printf("         t   Translate (char)                  ~   Not\n");
  printf("  PGUP   w   Write top to scratch pad          &   And\n");
  printf("  UP     x   Exchange top of stack             |   Or\n");
  printf("  HOME   z   Zero all state                    ^   Exclusive-or\n\n\n");

  printf("\n\nPress a key to continue...");
}

/****************************************************************/
/*								*/
/*	Draw_Prompt( string )					*/
/*								*/
/*		Write a message in the "wait" area.		*/
/*								*/
/****************************************************************/
void Draw_Prompt(string)
char *string;
{
  Move(WAIT_COLUMN, WAIT_LINE);
  Puts(Treverse);
  printf(string);
  Puts(Tnormal);
}

/****************************************************************/
/*								*/
/*	Erase_Prompt()						*/
/*								*/
/*		Erase the message in the "wait" area.		*/
/*								*/
/****************************************************************/
void Erase_Prompt()
{
  Move(WAIT_COLUMN, WAIT_LINE);
  printf("           ");
  Move(WAIT_COLUMN, WAIT_LINE);
}

/****************************************************************/
/*								*/
/*	Draw_Screen( state )					*/
/*								*/
/*		Redraw everything.				*/
/*								*/
/****************************************************************/
void Draw_Screen(s)
ic_state *s;
{
  Puts(Tclr_all);		/* Clear the screen  */
  Draw_Stack(s);
  Draw_Registers(s);
  Move(STATUS_COLUMN, STATUS_LINE);
  printf("Input base = %2d    ", s->input_base);
  if (s->output_base == ASCII)
	printf("Output is ASCII     ");
  else
	printf("Output base = %2d    ", s->output_base);
  if (s->scratch_pad != (FILE *)NULL) printf("Scratch file = %s", s->file_name);
  Move(WAIT_COLUMN, WAIT_LINE);
}

/****************************************************************/
/*								*/
/*	Draw_Stack( state )					*/
/*								*/
/*		Redraw the stack.				*/
/*								*/
/****************************************************************/
void Draw_Stack(s)
ic_state *s;
{
  int i;

  for (i = STACK_SIZE - 1; i >= 0; --i) {
	Move(STACK_COLUMN, STACK_LINE + STACK_SIZE - 1 - i);

	if (i >= s->stack_size)
		printf("%*c", s->output_base == BINARY ? 32 : 17, ' ');
	else
		Print_Number(stdout, s->stack[i], s->output_base);
  }

  Move(WAIT_COLUMN, WAIT_LINE);
}

/****************************************************************/
/*								*/
/*	Draw_Registers( state )					*/
/*								*/
/*		Redraw the registers. Note that only registers	*/
/*		in use are displayed. A register only drops	*/
/*		out of use after a 'z' command, which will	*/
/*		explicitly clear the display, thus we never	*/
/*		have to "wipe off" a value, as the		*/
/*		Draw_Stack() routine must.			*/
/*								*/
/****************************************************************/
void Draw_Registers(s)
ic_state *s;
{
  int i;

  for (i = 0; i < REGISTERS; ++i) {
	if ((1 << i) & s->register_mask) {
		Move(REG_COLUMN, REG_LINE + i);
		Print_Number(stdout, s->registers[i], s->output_base);
		printf(" (r%1d)", i);
	}
  }

  Move(WAIT_COLUMN, WAIT_LINE);
}

/****************************************************************/
/*								*/
/*	Draw_Top_of_Stack( state )				*/
/*								*/
/*		Redraw only the entry on the top of the stack.	*/
/*								*/
/****************************************************************/
void Draw_Top_of_Stack(s)
ic_state *s;
{
  Move(STACK_COLUMN, STACK_LINE + STACK_SIZE - 1);
  Print_Number(stdout, s->stack[0], s->output_base);
  Move(WAIT_COLUMN, WAIT_LINE);
}

/****************************************************************/
/*								*/
/*	Print_Number( stream, number, output_base )		*/
/*								*/
/*		Output the "number" to "stream" in the		*/
/*		specified "output_base".			*/
/*								*/
/****************************************************************/
void Print_Number(stream, number, output_base)
FILE *stream;
long int number;
int output_base;
{
  switch (output_base) {
      case HEXADECIMAL:
	fprintf(stream, "%12lx", number);
	break;

      case DECIMAL:
	fprintf(stream, "%12ld", number);
	break;

      case OCTAL:
	fprintf(stream, "%12lo", number);
	break;

      case BINARY:{
		unsigned long int mask;
		char pad = ' ';

		for (mask = 0x80000000; mask > 1; mask >>= 1)
			putc((mask & number) ? (pad = '0', '1') : pad, stream);

		putc((0x01 & number) ? '1' : '0', stream);

		break;
	}

      case ASCII:{
		char c = number & 0x7f;

		if ((number & ~0x7fL) == 0)
			fprintf(stream, "%15c", ' ');
		else
			fprintf(stream, "%12lx + ", number & ~0x7fL);

		if (c < ' ')
			fprintf(stream, "^%c", c + '@');
		else if (c == ' ')
			fprintf(stream, "sp");
		else if (c < 0x7f)
			fprintf(stream, " %c", c);
		else
			fprintf(stream, "^?");

		break;
	}

      default:
	fprintf(stderr, "Internal failure (output base)\n");
	Sigint(0);
  }
}
