
/* "UNTIL", a graphics editor,
   Copyright (C) 1985, 1990 California Institute of Technology.
   Original authors: Glenn Gribble, port by Steve DeWeerth
   Unix Port Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425 CU Boulder/Boulder CO 91125. 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (Version 1, 1989).

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
USA. */


/*******************************************************************************/
/*                                                                             */ 
/*  file contains stuff used to "walk" through the data lists                  */
/*  cleaned up by steve - 9 May 1990                                           */
/*                                                                             */ 
/*******************************************************************************/

#include <p2c/p2c.h>

#include "dispmod.h"

#ifndef SYSDEVS_H
#include <p2c/sysdevs.h>
#endif

#ifndef NEWKBD_H
#include <p2c/newkbd.h>
#endif

#ifndef NEWASM_H
#include <p2c/newasm.h>
#endif

#ifndef ASM_H
#include <p2c/asm.h>
#endif

#ifndef NEWCI_H
#include <p2c/newci.h>
#endif


/* global exported variables */

dispRec *dispHead;
short dispColor;
Char dispAssgn[256];
boolean dispExpert;



Static void showExpert(void)
{
  nk_screenhigh = 13056;   /* Red, inv, blink */
  if (dispExpert)
    nk_crtwritestr(0, 0, "*EXPERT*", 8);
  nk_screenhigh = 0;
  if (!dispExpert)
    nk_crtwritestr(0, 0, "", 8);
}


void dispRefresh(void)
{
  dispRec *p;

  p = dispHead;
  while (p != NULL) {
    ((void(*)(dispRec *dp, dispOp op))p->proc)(p, d_disp);
    p = p->next;
  }
  showExpert();
}


Static void dispArrRefresh(dispArrRec *ai)
{
  dispRec *p;

  p = dispHead;
  while (p != NULL) {
    if (p->kind == d_array && p->UU.U7.arrInfo == ai)
      ((void(*)(dispRec *dp, dispOp op))p->proc)(p, d_disp);
    p = p->next;
  }
}


Static void showhelp(void)
{
  printf("\f\n");
  printf("Commands for DISPLAY menus\n\n");
  printf(" ?               - help\n");
  printf(" +TAB,ENTER,DOWN - goto next entry\n");
  printf(" -TAB,UP         - goto previous entry\n");
  printf(" ->              - increment current value\n");
  printf(" <-              - decrement current value\n");
  printf(" CNTRL-C         - quit\n\n");
  printf("Special commands\n");
  printf(" ^E - toggle \"EXPERT\" mode \n");
  printf(" ^W - walk link\n");
  printf(" ^U - up (return to Walk point)\n");
  printf(" ^P - goto previous data item\n");
  printf(" ^N - goto next data item\n\n\n");
  printf("Press any key to continue");
  nk_getkey();
  printf("\f");
  dispRefresh();
}


Local void gotoPrevious(dispRec **cur)
{
  /* Backup.  Remember: the list is reversed. */
  ((void(*)(dispRec *dp, dispOp op))(*cur)->proc)(*cur, d_disp);
  
  do {
    *cur = (*cur)->next;
    if (*cur == NULL)
      *cur = dispHead;
  } while ((*cur)->kind == d_msg);
}

Local void gotoNext(dispRec **cur)
{
  dispRec *l, *p;

  /* Advance.  Remember: the list is reversed. */
  ((void(*)(dispRec *dp, dispOp op))(*cur)->proc)(*cur, d_disp);

  l = NULL;
  p = dispHead;

  while (p != *cur) {
    if (p->kind != d_msg)
      l = p;
    p = p->next;
  }

  /* Skip to last item in list */
  if (l == NULL) {
    while (p != NULL) {
      if (p->kind != d_msg)
	l = p;
      p = p->next;
    }
  }

  *cur = l;
}


void dispDisp(Char *terms_, Char *exit, dispRec **curElem)
{
  dispRec *cur, *l, *p;
  Char terms[256];
  Char ch;
  boolean quit;

  strcpy(terms, terms_);
  printf("\f");
  dispRefresh();

  cur = dispHead;
  while (cur != NULL && cur->kind == d_msg)
    cur = cur->next;
  if (cur == NULL) {
    printf("\007dispDisp: no display entries other than Messages.\n");
    _Escape(-1);
  }

  /* Find the current element specified by the user in curElem? */
  p = cur;
  while (p != NULL && p != *curElem)
    p = p->next;
  if (p != NULL && p->kind != d_msg)
    cur = p;

  do {
    quit = false;
    nk_gotoxy(cur->x, cur->y);
    ((void(*)(dispRec *dp, dispOp op))cur->proc)(cur, d_highlight);
    showExpert();
    ch = nk_getkey();
    if (strposc(terms, ch, 1) != 0)
      quit = true;
    else {
      switch (ch) {

      case '?':
	showhelp();
	break;

      case '\005':
	dispExpert = !dispExpert;
	break;

      case '\013':
	gotoPrevious(&cur);
	break;

      case '\037':
	if (cur->kind != d_array || cur->UU.U7.arrPos != 0)
	  gotoPrevious(&cur);
	else {
	  if (cur->UU.U7.arrInfo->top > 1) {
	    cur->UU.U7.arrInfo->top--;
	    dispArrRefresh(cur->UU.U7.arrInfo);
	  }
	}
	break;

      case '\015':
      case '\t':
	gotoNext(&cur);
	break;

      case '\n':
	if (cur->kind != d_array)
	  gotoNext(&cur);
	else {
	  if (cur->UU.U7.arrPos < cur->UU.U7.arrInfo->h - 1)
	    gotoNext(&cur);
	  else if (cur->UU.U7.arrInfo->top <= cur->UU.U7.arrInfo->numElem - cur->UU.U7.arrInfo->h) {
	    cur->UU.U7.arrInfo->top++;
	    dispArrRefresh(cur->UU.U7.arrInfo);
	  }
	}
	break;

      case '\b':
	((void(*)(dispRec *dp, dispOp op))cur->proc)(cur, d_dec);
	break;

      case '\034':
	((void(*)(dispRec *dp, dispOp op))cur->proc)(cur, d_inc);
	break;

      case '\003':
	quit = true;
	break;

      default:
	if (cur->expert && !dispExpert)
	  BEEP();
	else {
	  nk_ungetkey(ch);
	  nk_screenhigh = 0;
	  if (cur->f != 0)
	    nk_crtwritestr(cur->x, cur->y, "", cur->f);
	  gets(dispAssgn);
	  ((void(*)(dispRec *dp, dispOp op))cur->proc)(cur, d_assign);
	  gotoNext(&cur);
	}
	break;
      }  /* case */
    }
  } while (!quit);

  /* Inform user */
  *exit = ch;
  *curElem = cur;
}


void dispBegin(void)
{
  dispHead = NULL;
  dispColor = 0;
  dispExpert = false;
}


dispRec *dispMake(short x, short y, short f, void *proc, dispKinds kind)
{
  dispRec *p;

  switch (kind) {
  /* cases merged due to no special Malloc forms */
  case d_msg:
  case d_int:
  case d_sint:
  case d_str:
  case d_enum:
  case d_slist:
  case d_char:
  case d_array:
    p = Malloc(sizeof(dispRec));
    break;
  }

  p->x = x;
  p->y = y;
  p->f = f;
  p->proc = proc;
  p->kind = kind;
  p->color = dispColor;
  p->next = dispHead;
  dispHead = p;
  p->expert = dispExpert;

  return p;
}


Static void crtWriteInt(long x, long y, long i, long f)
{
  Char s[21];
  long p;

  sprintf(s, "%ld", i);
  p = strlen(s) + 1;
  nk_crtwritestr(x, y, s, f);
}


/* Index into a comma-separated string */
Static Char *findStrSub(Char *Result, Char *names_, short subMode)
{
  Char names[256];
  long p;

  strcpy(names, names_);

  while (subMode > 0 && *names != '\0') {
    p = strpos2(names, ",", 1);
    if (p == 0)
      p = strlen(names);
    strcpy(names, names + p);
    subMode--;
  }

  p = strpos2(names, ",", 1);
  if (p != 0)
    names[p - 1] = '\0';

  return strcpy(Result, names);
}


Static short numCommas(Char *s)
{
  short nc, i;

  nc = 0;

  for (i = 0 ; i < strlen(s) ; i++) {
    if (s[i] == ',')
      nc++;
  }

  return nc;
}


Static Char *dispOp_NAMES[] = {
  "D_NOP", "D_DISP", "D_HIGHLIGHT", "D_INC", "D_DEC", "D_SET", "D_ASSIGN"
} ;


Static void dispDefault(dispRec *dp, dispOp op)
{
  short val;   /* inc=1, set=0, dec=-1 */
  Char tmps[256];
  short t;
  long i, p;
  Char ch;
  Char *STR1;
  Char STR2[256];
  Char STR3[256];
  short FORLIM;
  dispArrRec *WITH1;
  dispArrElem *WITH2;

  if (dp->expert && !dispExpert &&
      ((1L << ((long)op)) & ((1L << ((long)d_assign)) |
	 (1L << ((long)d_inc)) | (1L << ((long)d_dec)))) != 0) {
    op = d_nop;
    BEEP();
  }

  if (op == d_assign) {
    switch (dp->kind) {

    case d_array:
      printf("Can't %s a D_ARRAY!!\007\n", dispOp_NAMES[(long)op]);
      break;

    case d_msg:
      printf("Can't %s a D_MSG!!\007\n", dispOp_NAMES[(long)op]);
      break;

    case d_str:
      if (strlen(dispAssgn) > dp->UU.U3.strMaxLen)
	dispAssgn[dp->UU.U3.strMaxLen] = '\0';
      strcpy(dp->UU.U3.strCur, dispAssgn);
      break;

    case d_int:
      i = strtol(dispAssgn, &STR1, 10);
      p = STR1 - dispAssgn + 1;
      *dp->UU.U1.icur = i;
      break;

    case d_sint:
      i = strtol(dispAssgn, &STR1, 10);
      p = STR1 - dispAssgn + 1;
      if (i >= -32768L && i < 32767)
	*dp->UU.U2.shcur = i;
      break;

    case d_enum:
      i = -1;
      strlower(dispAssgn, dispAssgn);
      for (t = dp->UU.U4.elow ; t <= dp->UU.U4.ehigh ; t++) {
	if (!strcmp(strlower(STR2, findStrSub(STR3, dp->UU.U4.enames, t)), dispAssgn))
	  i = t;
      }
      if (i != -1)
	*dp->UU.U4.ecur = (Char)i;
      break;

    case d_slist:
      i = -1;
      strlower(dispAssgn, dispAssgn);
      for (t = 1 ; t <= dp->UU.U5.sListMax ; t++) {
	if (!strcmp(strlower(STR2, dp->UU.U5.sList[t - 1]), dispAssgn))
	  i = t;
      }
      if (i != -1)
	*dp->UU.U5.sListCur = i;
      break;
    }  /* case kind */

    op = d_set;   /* Now range-check */
  }


  if (op == d_inc)
    val = 1;
  else if (op == d_dec)
    val = -1;
  else
    val = 0;

  switch (op) {

  case d_nop:
    /* blank case */
    break;

  case d_inc:
  case d_dec:
  case d_set:
    switch (dp->kind) {

    case d_array:
      printf("Can't %s a D_ARRAY!!\007\n", dispOp_NAMES[(long)op]);
      break;

    case d_msg:
      printf("Can't %s a D_MSG!!\007\n", dispOp_NAMES[(long)op]);
      break;

    case d_str:
      if (op != d_set) {
	dp->UU.U3.strDefCur += val;

	if (dp->UU.U3.strDefCur < 0)
	  dp->UU.U3.strDefCur = 0;
	else if (dp->UU.U3.strDefCur > numCommas(dp->UU.U3.strDef))
	  dp->UU.U3.strDefCur = numCommas(dp->UU.U3.strDef);

	findStrSub(tmps, dp->UU.U3.strDef, dp->UU.U3.strDefCur);
	if (*dp->UU.U3.strDef == '\0') {
	  strcpy(tmps, dp->UU.U3.strCur);
	  p = 1;
	  newci_inputstring(tmps, im_keep, "\015", &ch, false, &p);
	}

	if (strlen(tmps) > dp->UU.U3.strMaxLen)
	  tmps[dp->UU.U3.strMaxLen] = '\0';

	strcpy(dp->UU.U3.strCur, tmps);
      }
      break;

    case d_int:
      *dp->UU.U1.icur += val;
      if (*dp->UU.U1.icur > dp->UU.U1.ihigh)
	*dp->UU.U1.icur = dp->UU.U1.ihigh;
      else if (*dp->UU.U1.icur < dp->UU.U1.ilow)
	*dp->UU.U1.icur = dp->UU.U1.ilow;
      break;

    case d_sint:
      *dp->UU.U2.shcur += val;
      if (*dp->UU.U2.shcur > dp->UU.U2.shhigh)
	*dp->UU.U2.shcur = dp->UU.U2.shhigh;
      else if (*dp->UU.U2.shcur < dp->UU.U2.shlow)
	*dp->UU.U2.shcur = dp->UU.U2.shlow;
      break;

    case d_enum:
      t = *dp->UU.U4.ecur + val;
      if (t > dp->UU.U4.ehigh)
	t = dp->UU.U4.ehigh;
      else if (t < dp->UU.U4.elow)
	t = dp->UU.U4.elow;
      *dp->UU.U4.ecur = t;
      break;

    case d_slist:
      t = *dp->UU.U5.sListCur + val;
      if (t < 1)
	t = dp->UU.U5.sListMax;
      else if (t > dp->UU.U5.sListMax)
	t = 1;
      *dp->UU.U5.sListCur = t;
      break;
    }  /* case kind */
    break;

  case d_disp:
  case d_highlight:
    nk_screenhigh = 0;
    if (dp->f != 0)
      nk_crtwritestr(dp->x, dp->y, "", dp->f);
    nk_screenhigh = dp->color;
    if (op == d_highlight)
      nk_screenhigh = nc_yellow;
    switch (dp->kind) {

    case d_array:
      WITH1 = dp->UU.U7.arrInfo;
      WITH2 = &WITH1->arr[WITH1->top + dp->UU.U7.arrPos - 1];
      nk_screenhigh = WITH2->color;
      if (op == d_highlight)
	nk_screenhigh += 256;
      nk_crtwritestr(WITH1->x, WITH1->y + dp->UU.U7.arrPos, WITH2->str, 0);
      break;

    case d_msg:
      nk_crtwritestr(dp->x, dp->y, dp->UU.msgp, 0);
      break;

    case d_str:
      nk_crtwritestr(dp->x, dp->y, dp->UU.U3.strCur, 0);
      break;

    case d_int:
      crtWriteInt(dp->x, dp->y, *dp->UU.U1.icur, 0);
      break;

    case d_sint:
      crtWriteInt(dp->x, dp->y, *dp->UU.U2.shcur, 0);
      break;

    case d_enum:
      nk_crtwritestr(dp->x, dp->y, findStrSub(STR3, dp->UU.U4.enames, *dp->UU.U4.ecur), 0);
      break;

    case d_slist:
      nk_crtwritestr(dp->x, dp->y, dp->UU.U5.sList[*dp->UU.U5.sListCur - 1], 0);
      break;
    }  /* case kind */

    break;
  }  /* case op */
}


void dispAddStr(short x, short y, short f, Char *c, Char *defaults)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_str);
  p->UU.U3.strCur = c;
  p->UU.U3.strMaxLen = 255;
  p->UU.U3.strDefCur = 0;
  p->UU.U3.strDef = malloc(strlen(defaults) + 1);
  strcpy(p->UU.U3.strDef, defaults);
}


void dispAddMsg(short x, short y, short f, Char *m)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_msg);
  p->UU.msgp = malloc(strlen(m) + 1);
  strcpy(p->UU.msgp, m);
}


void dispAddPtr(short x, short y, short f, void **i)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_int);
  p->UU.U1.icur = (long *)i;
  p->UU.U1.ilow = LONG_MIN;
  p->UU.U1.ihigh = LONG_MAX;
}


void dispAddInt(short x, short y, short f, long *i, long low, long high)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_int);
  p->UU.U1.icur = i;
  p->UU.U1.ilow = low;
  p->UU.U1.ihigh = high;
}


void dispAddShort(short x, short y, short f, short *i, short low, short high)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_sint);
  p->UU.U2.shcur = i;
  p->UU.U2.shlow = low;
  p->UU.U2.shhigh = high;
}


void dispAddEnum2(short x, short y, short f, short *e, short high, Char *names)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_enum);

  if (bigendian)
    p->UU.U4.ecur = (Char *)((char *)e + 3);
  else
    p->UU.U4.ecur = (Char *)((char *)e);


/* p2c: dispmod.text, line 540:
 * Note: Don't know how to reduce offset for ADDR [175] */
  p->UU.U4.elow = '\0';
  p->UU.U4.ehigh = high;
  p->UU.U4.enames = malloc(strlen(names) + 1);
  strcpy(p->UU.U4.enames, names);
}


void dispAddSList(short x, short y, short f, short *e, short high, Char **names)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_slist);
  p->UU.U5.sList = names;
  p->UU.U5.sListMax = high;
  p->UU.U5.sListCur = e;
}


void dispAddEnum1(short x, short y, short f, Char *e, short high, Char *names)
{
  dispRec *p;

  p = dispMake(x, y, f, dispDefault, d_enum);
  p->UU.U4.ecur = e;
  p->UU.U4.elow = '\0';
  p->UU.U4.ehigh = high;
  p->UU.U4.enames = malloc(strlen(names) + 1);
  strcpy(p->UU.U4.enames, names);
}


void dispAddArr(short px, short py, short pw, short ph, short num, short alloc, dispArrElem *ap)
{
  dispRec *p;
  dispArrRec *ai;
  long i;

  ai = Malloc(sizeof(dispArrRec));
  ai->x = px;
  ai->y = py;
  ai->w = pw;
  ai->h = ph;
  ai->top = 1;
  ai->top = 3;
  ai->arr = ap;
  ai->numElem = num;
  ai->allocElem = alloc;

  for (i = 0; i < ph; i++) {
    p = dispMake(px, py + i, pw, dispDefault, d_array);
    p->UU.U7.arrInfo = ai;
    p->UU.U7.arrPos = i;
  }
}
