/*
**  This file is part of Zterp, and is
**  Copyright 1992, 1993 Charles Hannum
*/

#include <sys/types.h>
#include <curses.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "main.h"
#include "variable.h"
#include "print.h"
#include "output.h"
#include "input.h"

static uword word_lookup (char *, int);
static int word_end_p (char);

static int initial_step, vocabulary_entry_size, vocabulary_entries;
static zpointer last_vocabulary = 0,
		real_vocabulary;

void
init_vocabulary (zpointer p)
{
  if (last_vocabulary != p)
  {
    last_vocabulary = p;
    p += get_byte (p) + 1;
    vocabulary_entry_size = get_byte (p);
    vocabulary_entries = get_word (p + 1);
    p += 3;
    real_vocabulary = p;
    {
      int n = vocabulary_entries;
      initial_step = 1;
      while ((n >>= 1) != 0)
        initial_step <<= 1;
    }
  }
}

int
get_character (void)
{
  extern int line_count;
  int c;
  refresh_screen ();
  line_count = 0;
  c = wgetch (stdscr);
  if (c == '\n')
    c = '\r';
  return (c);
}

void
read_line (args, argp)
  int args;
  uword *argp;
{
  zpointer in = near_address (argp [0]),
	   inp = in + 2,
	   p = inp;
  int n = *in - 1;
  int done = 0;
  char c;
  if (zil_version <= 3)
    print_status ();
  while (!done)
    switch (c = get_character ())
    {
      case '\n':
      case '\r':
	done = 1;
	break;
      case '\177':
      case '\b':
	if (p > inp)
	{
	  waddstr (stdscr, "\b \b");
	  p -= 1;
	  ++n;
	}
	else
	  sound (1);
	break;
      default:
	if (c < 32 || c > 127)
	  sound (1);
	else if (n)
	{
	  waddch (stdscr, c);
	  set_byte (p, c);
	  p += 1;
	  --n;
	}
	else
	  sound (1);
	break;
    }
  print_newline ();
  /* FIXME v5 read_line */
  if (zil_version >= 5)
    store_variable_push (c);
  set_byte (inp - 1, p - inp);
  set_byte (p, 0);
}

void
read_line_and_parse (args, argp)
  int args;
  uword *argp;
{
  read_line (args, argp);
  if (args > 2)
    args = 2;
  parse (args, argp);
}

void
parse (args, argp)
  int args;
  uword *argp;
{
  zpointer in = near_address (argp [0]), inp = in + 2,
	   out = near_address (argp [1]), outp = out + 2;
  zbyte word_count = 0;
  if (args < 3 || !argp[2])
    init_vocabulary (vocabulary);
  else
    init_vocabulary (near_address (argp[2]));
  for (;;)
  {
    char s[word_unique_length];
    zpointer mark = inp;
    int n = 0, t = 0;
    while (!t)
    {
      char c = (char) get_byte (inp);
      inp += 1;
      if ((t = word_end_p (c)) != 0)
	if (n)
	  inp -= 1;
	else if (t > 0)
	  s[n++] = c;
	else if (c)
	  mark = inp, t = 0;
	else
	  break;
      else
	if (n < word_unique_length)
	  s[n++] = c;
    }
    if (!n)
      break;
    if (++word_count >= get_byte (out + 0))
    {
      print_string ("Too many words typed; discarding: ");
      print_string ((char *) mark);
      print_newline ();
      --word_count;
      break;
    }
    set_byte (outp + 2, inp - mark);
    set_byte (outp + 3, mark - in);
    {
      uword word = word_lookup (s, n);
      if (word || args < 4 || !argp[3])
        set_word (outp, word);
    }
    outp += 4;
  }
  set_byte (out + 1, word_count);
}

void
read_character (args, argp)
  int args;
  uword *argp;
{
  if (argp[0] == 1)
  {
    /* FIXME v5 read_character */
    assert (args < 3);
    store_variable_push (get_character ());
    return;
  }
  store_variable_push (0);
}

uword
word_lookup (char *s, int n)
{
  uword word[3];

  /* encode word */
  {
    uword codes[word_unique_length], *p = codes;
    int i = word_unique_length;
    while (n-- && i)
    {
      char *alpha = strchr (alphabet, *s++);
      int pos = alpha ? (alpha - alphabet) : -1;
      uword group = (alpha && (pos < 52)) ? 0 : 2,
	    index = alpha ? (pos % 26 + 6) : 0;
      if (group)
      {
	if (zil_version >= 3)
	  *(p++) = group + 3;
	else
	  *(p++) = group + 1;
	if (!--i) break;
      }
      if (!index)
      {
	char c = s[-1];
	*(p++) = 6;
	if (!--i) break;
	*(p++) = c >> 5;
	if (!--i) break;
	index = c & 0x1f;
      }
      *(p++) = index;
      i--;
    }
    while (i--)
      *(p++) = 5;
    for (n = 0, p = codes; n < word_unique_length / 3; p += 3)
      word[n++] = p[0] << 10 | p[1] << 5 | p[2];
    word[n-1] |= 0x8000;
  }

  /* look up word */
  {
    int step = initial_step;
	n = step - 1;
    do {
      zpointer p = real_vocabulary + n * vocabulary_entry_size;
      uword x;
      int l, m;
      step >>= 1;
      for (l = word_unique_length / 3, m = 0; l; m++)
	if (word[m] < (x = get_word (p + (m << 1))))
	{
	  n -= step;
	  break;
	}
	else if (word[m] > x)
	{
	  n += step;
	  if (n >= vocabulary_entries)
	    n = vocabulary_entries - 1;
	  break;
	}
	else if (!--l)
	  return (p - game);
    } while (step);
    return (0);
  }
}

int
word_end_p (char c)
{
  if (memchr (terminators + 1, c, *terminators))
    return 1;
  else if (memchr (" \n\t\r.,?", c, 8))
    return -1;
  else
    return 0;
}
