/* wm_test.c (emx+gcc) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/video.h>
#include <sys/winmgr.h>
#include <sys/kbdscan.h>

#define FALSE 0
#define TRUE  1

#define MAX_WINDOWS 100
#define PUTSA_LEN 20

extern char *_v_mem;

static wm_handle w[MAX_WINDOWS];
static int c, top, sel, n, windows, auto_mode, auto_win;
static int width, height;
static char auto_c;
static wm_handle helpw = NULL;
static int move_flag;
static int counter = 1;
static int title_flag, wrap_flag;
static int cursor_flag;
static int putca_flag;
static int find_x, find_y;
static char putsa_str[2*PUTSA_LEN] =
{
  'H', 7, 'e', 6, 'l', 5, 'l', 4, 'o', 3, '*', 0,
  'w', 7, 'i', 6, 'n', 5, 'd', 4, 'o', 3, 'w', 2, '*', 0,
  'm', 7, 'a', 6, 'n', 5, 'a', 4, 'g', 3, 'e', 2, 'r', 1
};


char buffer[22];

static void error (const char *msg)
{
  printf ("\n%s\n\a", msg);
  exit (1);
}


static int last_key = -1;

static int getch (void)
{
  int c;

  if (last_key >= 0)
    {
      c = last_key;
      last_key = -1;
    }
  else
    c = _read_kbd (0, 1, 0);
  return c;
}


static int ready (void)
{
  if (last_key < 0)
    last_key = _read_kbd (0, 0, 0);
  return last_key >= 0;
}


static void beep (int f, int d)
{
  putchar ('\a');
}


static void move (int xi, int yi)
{
  int x, y;

  if (move_flag)
    {
      wm_get_pos (w[sel], &x, &y);
      wm_move (w[sel], x+xi, y+yi);
    }
  else
    {
      wm_getxy (w[sel], &x, &y);
      wm_gotoxy (w[sel], x+xi, y+yi);
    }
}


static void disp (void)
{
  if (w[1] != NULL)
    {
      wm_gotoxy (w[1], 0, 14);
      wm_printf (w[1], "Selected: %2d, top: %2d, counter: %2d",
                 sel, top, counter);
    }
}


static void disp_move (void)
{
  int a;

  if (w[1] != NULL)
    {
      a = wm_get_attrib (w[1]);
      if (!move_flag)
        wm_attrib (w[1], F_BLUE|B_BLACK|INTENSITY);
      else
        wm_attrib (w[1], F_WHITE|B_BLACK);
      wm_puts_at (w[1], 15, 13, "cursor");
      wm_attrib (w[1], a);
      wm_puta_at (w[1], 24, 13,
                  move_flag ? F_BLUE|B_BLACK|INTENSITY : F_WHITE|B_BLACK,
                  6);
    }
}


static void help_clear (void)
{
  if (helpw != NULL)
    {
      wm_clear (helpw);
      wm_gotoxy (helpw, 0, 0);
    }
}


static void help_disp (const char *s)
{
  if (helpw != NULL)
    wm_puts (helpw, s);
}


static void help_open (void)
{
  helpw = wm_create (20, 8, 60, 16, ' ', B_RED|F_BLACK,
                     F_BLUE|B_BLACK|INTENSITY);
  if (helpw != NULL)
    wm_open (helpw);
}


static void help_close (void)
{
  if (helpw != NULL)
    {
      wm_close (helpw);
      wm_delete (helpw);
      helpw = NULL;
    }
}


static int get_xy (int *xx, int *yy, int minx, int maxx, int miny, int maxy)
{
  int c;
  int x, y;

  x = *xx; y = *yy;
  wm_chide (FALSE);
  do
    {
      v_gotoxy (x, y);
      c = getch ();
      switch (c)
        {
        case 0:
          switch (getch ())
            {
            case K_UP:
              if (y >= miny+1)
                --y;
              break;
            case K_DOWN:
              if (y <= maxy-1)
                ++y;
              break;
            case K_LEFT:
              if (x >= minx+1)
                --x;
              break;
            case K_RIGHT:
              if (x <= maxx-1)
                ++x;
              break;
            case K_PAGEUP:
              if (y >= miny+4)
                y -= 4;
              else
                y = miny;
              break;
            case K_PAGEDOWN:
              if (y <= maxy-4)
                y += 4;
              else
                y = maxy;
              break;
            case K_CTRL_LEFT:
              if (x >= minx+8)
                x -= 8;
              else
                x = minx;
              break;
            case K_CTRL_RIGHT:
              if (x <= maxx-8)
                x += 8;
              else
                x = maxx;
              break;
            case K_HOME:
              x = minx;
              break;
            case K_END:
              x = maxx;
              break;
            case K_CTRL_HOME:
            case K_CTRL_PAGEUP:
              y = miny;
              break;
            case K_CTRL_END:
            case K_CTRL_PAGEDOWN:
              y = maxy;
              break;
            }
          break;
        case 0x1b:
          wm_chide (cursor_flag);
          return FALSE;
        }
    } while (c != '\r');
  *xx = x; *yy = y;
  wm_chide (cursor_flag);
  return TRUE;
}


static int get_attr (int *dst)
{
  int color[2];
  int i, intens, c;

  for (i = 0; i < 2; ++i)
    {
      if (i == 0)
        help_disp ("Background color");
      else
        help_disp ("Foreground color");
      help_disp (" (0..7): ");
      do
        {
          c = getch ();
          if (c == 0x1b)
            return FALSE;
        } while (c < '0' || c > '7');
      color[i] = c-'0';
      if (helpw != NULL)
        {
          wm_putc (helpw, (char)c);
          wm_putc (helpw, '\n');
        }
    }
  help_disp ("Intensified or Normal:");
  intens = -1;
  do
    {
      c = getch ();
      switch (c)
        {
        case 0x1b:
          return FALSE;
        case 'i':
        case 'I':
          intens = 8;
          break;
        case 'n':
        case 'N':
          intens = 0;
          break;
        }
    } while (intens < 0);
  if (helpw != NULL)
    {
      wm_putc (helpw, (char)c);
      wm_putc (helpw, '\n');
    }
  *dst = 16*color[0]+color[1]+intens;
  return TRUE;
}


static void new_window (void)
{
  int x0, y0, x, y, border, battr, wattr, c;
  wm_handle neww;

  if (windows >= MAX_WINDOWS)
    return;
  help_open ();
  help_clear ();
  help_disp ("Use arrow keys to move cursor\n");
  help_disp ("to upper left corner of the \n");
  help_disp ("new window, then press return.");
  x0 = 0; y0 = 0;
  if (!get_xy (&x0, &y0, 0, width-11, 0, height-5))
    {
      help_close ();
      return;
    }
  help_clear ();
  help_disp ("Use arrow keys to move cursor\n");
  help_disp ("to lower right corner of the\n");
  help_disp ("new window, then press return.");
  x = x0+9; y = y0+3;
  if (!get_xy (&x, &y, x, width-1, y, height-1))
    {
      help_close ();
      return;
    }
  help_clear ();
  help_disp ("Enter border style:\n");
  help_disp ("            ÚÄ¿     ÉÍ»     ÕÍ¸     ÖÄ·\n");
  help_disp ("F10=none F1=ÀÄÙ  F2=ÈÍ¼  F3=ÔÍ¾  F4=ÓÄ½\n");
  help_disp ("Any other character is used as\n");
  help_disp ("border character\n");
  border = -1;
  do
    {
      c = getch ();
      switch (c)
        {
        case 0:
          c = getch ();
          switch (c)
            {
            case K_F10:
              border = 0;
              break;
            case K_F1:
              border = 1;
              break;
            case K_F2:
              border = 2;
              break;
            case K_F3:
              border = 3;
              break;
            case K_F4:
              border = 4;
              break;
            }
          break;
        case 0x1b:
          help_close ();
          return;
        default:
          border = c;
          break;
        }
    } while (border < 0);
  if (border != 0)
    {
      help_clear ();
      help_disp ("Border attribute:\n");
      if (!get_attr (&battr))
        {
          help_close ();
          return;
        }
    }
  help_clear ();
  help_disp ("Text attribute:\n");
  if (!get_attr (&wattr))
    {
      help_close ();
      return;
    }
  help_close ();
  neww = wm_create (x0, y0, x, y, border, battr, wattr);
  if (neww != NULL)
    {
      sel = windows;
      w[windows++] = neww;
      disp ();
    }
}


static void do_putc (int win, char c)
{
  if (putca_flag)
    wm_putca (w[win], c, BW_REVERSE);
  else
    wm_putc (w[win], c);
}


static void do_auto (void)
{
  if (auto_win >= windows)
    auto_win = 0;
  if (auto_win == 1)
    ++auto_win;
  if (auto_win < windows && w[auto_win] != NULL)
    {
      do_putc (auto_win, auto_c);
      if (auto_c >= (char)126)
        auto_c = ' ';
      else
        ++auto_c;
    }
  ++auto_win;
}


static int rnd_attr (int old)
{
  int f, b, i, a;

  do
    {
      f = rand () % 8;
      b = rand () % 8;
      i = rand () % 2;
      a = f+i*8+b*16;
    } while ((f == b && i == 0) || a == old);
  return a;
}


static void rnd_window (void)
{
  wm_handle neww;
  int x0, y0, x1, y1, border, battr, wattr;

  do
    {
      x0 = rand () % width; x1 = rand () % width;
      y0 = rand () % height; y1 = rand () % height;
    } while (x0+x1 >= width || y0+y1 >= height || x1 < 1 || y1 < 1);
  border = rand () % 5;
  battr = rnd_attr (0);
  wattr = rnd_attr (battr);
  if (windows < MAX_WINDOWS)
    {
      neww = wm_create (x0, y0, x0+x1, y0+y1, border, battr, wattr);
      if (neww != NULL)
        {
          sel = windows;
          w[windows++] = neww;
          wm_open (neww);
          wm_cursor (neww);
          disp ();
        }
    }
}


static void make_top (void)
{
  do
    {
      ++top;
      if (top >= windows)
        top = 0;
    } while (w[top] == NULL);
  wm_top (w[top]);
  disp ();
}


static void next_window (void)
{
  do
    {
      ++sel;
      if (sel >= windows)
        sel = 0;
    } while (w[sel] == NULL);
  wm_cursor (w[sel]);
  disp ();
}


int main (int argc, char *argv[])
{
  int i, x, y, a, f, no_mem_flag;

  no_mem_flag = 0;
  if (argc == 2 && strcmp (argv[1], "-n") == 0)
    no_mem_flag = 1;
  auto_mode = FALSE; move_flag = 0; helpw = NULL;
  title_flag = TRUE; wrap_flag = TRUE; cursor_flag = TRUE;
  strcpy (buffer, "<No input>");
  v_init (); v_dimen (&width, &height);
  find_x = width/2; find_y = height/2;
  if (!wm_init (MAX_WINDOWS))
    error ("wm_init failed");
  if (no_mem_flag)
    _v_mem = NULL;
  windows = 9; counter = 1;
  w[0] = wm_create (10, 5, 60, 15, 3, B_BLACK|F_GREEN, B_BLACK|F_WHITE);
  w[1] = wm_create (15, 9, 65, 23, 1, B_BLACK|F_CYAN, B_BLACK|F_WHITE);
  w[2] = wm_create (0, 0, 14, 13, 2, B_CYAN|F_RED, B_CYAN|F_WHITE|INTENSITY);
  w[3] = wm_create (-10, -10, 100, 30, 2, B_BROWN|F_YELLOW|INTENSITY,
                    B_GREEN|F_YELLOW|INTENSITY);
  w[4] = wm_create (30, 18, 55, 23, 0, B_BLACK|F_WHITE,
                    B_GREEN|F_WHITE|INTENSITY);
  w[5] = wm_create (25, 8, 70, 18, 2, B_BLACK|F_YELLOW, B_BLACK|F_RED);
  w[6] = wm_create (0, 19, 14, 24, 2, B_CYAN|F_RED, B_CYAN|F_WHITE|INTENSITY);
  w[7] = wm_create (60, 0, 79, 13, 2, B_CYAN|F_RED, B_CYAN|F_WHITE|INTENSITY);
  w[8] = wm_create (60, 19, 79, 24, 4, B_CYAN|F_RED, B_CYAN|F_WHITE|INTENSITY);
  for (i = 0; i < windows; ++i)
    if (w[i] == NULL)
      error ("wm_create failed");
  top = 1; sel = 0;
  wm_top (w[top]);
  for (i = 0; i < windows; i++)
    if (i != 3)
      wm_open (w[i]);
  wm_puts (w[1], "Esc #Q #A Quit; wm_close_all; set attribute\n");
  wm_puts (w[1], "#T ^Pg Pg Next to top; top/bottom; up/down\n");
  wm_puts (w[1], "^F ^B     Forward/back one window (select)\n");
  wm_puts (w[1], "#O #C #F  Open/close current window; find\n");
  wm_puts (w[1], "^D #B     Delete current window; change border\n");
  wm_puts (w[1], "#S ^L #G  Clr window; clr to eol; wm_attrib_all\n");
  wm_puts (w[1], "^H ^I ^E  Backspace; input/display string\n");
  wm_puts (w[1], "F2 F3 F4  Cursor mode; Cursor off/on\n");
  wm_puts (w[1], "^A ^N ^R  Auto mode; create new/random window\n");
  wm_puts (w[1], "Ins Del   Insert/delete character\n");
  wm_puts (w[1], "#I #D     Insert/delete line\n");
  wm_puts (w[1], "#P #M #W  Inc/dec counter; toggle wrap\n");
  wm_puts (w[1], "F5 F6 F7  wm_putc[a]; update false; true\n");
  wm_puts (w[1], "Arrows    Move cursor / window (F1)\n");
  wm_putc_at (w[6], 1, 1, '1');
  wm_putca_at (w[6], 2, 2, '2', BW_REVERSE);
  wm_gotoxy (w[7], 8, 1);
  wm_putsa (w[7], putsa_str, PUTSA_LEN);
  wm_putsa_at (w[7], 8, 4, putsa_str, PUTSA_LEN);
  wm_putsa_at (w[7], 8, 13, putsa_str, PUTSA_LEN);
  disp_move ();
  wm_attrib (w[1], B_RED|F_YELLOW|INTENSITY);
  wm_cursor (w[sel]);
  disp ();
  do
    {
      while (!ready ())
        {
          if (auto_mode)
            do_auto ();
        }
      c = getch ();
      if (auto_mode)
        wm_cursor (w[sel]);
      switch (c)
        {
        case 0:
          switch (getch ())
            {
            case K_ALT_A:                                          /* Alt-A */
              help_open ();
              help_clear ();
              help_disp ("New attribute:\n");
              if (get_attr (&a))
                wm_attrib (w[sel], a);
              help_close ();
              break;
            case K_ALT_B:                                          /* Alt-B */
              f = getch ();
              if (f == 0) getch ();
              a = getch ();
              if (a == 0) getch ();
              wm_border (w[sel], f, a, buffer, title_flag, 15);
              title_flag = !title_flag;
              break;
            case K_ALT_C:                                          /* Alt-C */
              wm_close (w[sel]);
              break;
            case K_ALT_D:                                          /* Alt-D */
              wm_del_line (w[sel], wm_gety (w[sel]), counter);
              break;
            case K_ALT_F:                                          /* Alt-F */
              if (get_xy (&find_x, &find_y, 0, width-1, 0, height-1))
                {
                  wm_handle wh;
                  int i, wx, wy;

                  wh = wm_find (find_x, find_y);
                  if (wh == NULL)
                    beep (880, 200);
                  else
                    {
                      for (i = 0; i < windows; ++i)
                        if (w[i] == wh)
                          break;
                      if (i >= windows)
                        beep (440, 400);
                      else
                        {
                          wm_get_pos (wh, &wx, &wy);
                          wm_putc_at (wh, find_x-wx, find_y-wy, '*');
                        }
                    }
                }
              break;
            case K_ALT_G:                                          /* Alt-G */
              wm_attrib_all (w[sel], wm_get_attrib (w[sel]));
              break;
            case K_ALT_I:                                          /* Alt-I */
              wm_ins_line (w[sel], wm_gety (w[sel]), counter);
              break;
            case K_ALT_M:                                          /* Alt-M */
              if (counter > 1)
                --counter;
              disp ();
              break;
            case K_ALT_O:                                          /* Alt-O */
              wm_open (w[sel]);
              break;
            case K_ALT_P:                                          /* Alt-P */
              if (counter < 99)
                ++counter;
              disp ();
              break;
            case K_ALT_Q:                                          /* Alt-Q */
              wm_close_all ();
              break;
            case K_ALT_S:                                          /* Alt-S */
              wm_clear (w[sel]);
              break;
            case K_ALT_T:                                          /* Alt-T */
              make_top ();
              break;
            case K_ALT_W:                                          /* Alt-W */
              wrap_flag = !wrap_flag;
              wm_wrap (w[sel], wrap_flag);
              break;
            case K_UP:                                          /* Up arrow */
              move (0, -1);
              break;
            case K_DOWN:                                      /* Down arrow */
              move (0, 1);
              break;
            case K_LEFT:                                      /* Left arrow */
              move (-1, 0);
              break;
            case K_RIGHT:                                    /* Right arrow */
              move (1, 0);
              break;
            case K_HOME:                                            /* Home */
              wm_gotoxy (w[sel], 0, 0);
              break;
            case K_END:                                              /* End */
              wm_dimen (w[sel], &x, &y);
              wm_gotoxy (w[sel], x-1, y-1);
              break;
            case K_PAGEDOWN:                                   /* Page Down */
              wm_down (w[sel]);
              break;
            case K_PAGEUP:                                       /* Page Up */
              wm_up (w[sel]);
              break;
            case K_INS:                                              /* Ins */
              wm_getxy (w[sel], &x, &y);
              wm_ins_char (w[sel], x, y, counter);
              break;
            case K_DEL:                                              /* Del */
              wm_getxy (w[sel], &x, &y);
              wm_del_char (w[sel], x, y, counter);
              break;
            case K_CTRL_PAGEUP:                                /* Ctrl-PgUp */
              top = sel-1;
              make_top ();
              break;
            case K_CTRL_PAGEDOWN:                              /* Ctrl-PgDn */
              wm_bottom (w[sel]);
              break;
            case K_F1:                                                /* F1 */
              move_flag = !move_flag;
              disp_move ();
              break;
            case K_F2:                                                /* F2 */
              cursor_flag = !cursor_flag;
              wm_chide (cursor_flag);
              break;
            case K_F3:                                                /* F3 */
              wm_cvis (w[sel], FALSE);
              break;
            case K_F4:                                                /* F4 */
              wm_cvis (w[sel], TRUE);
              break;
            case K_F5:                                                /* F5 */
              putca_flag = !putca_flag;
              break;
            case K_F6:                                                /* F6 */
              wm_update (w[sel], FALSE);
              break;
            case K_F7:                                                /* F7 */
              wm_update (w[sel], TRUE);
              break;
            }
          break;
        case 0x01:                                            /* Ctrl-A */
          auto_mode = !auto_mode;
          auto_win = 1;
          auto_c = ' ';
          break;
        case 0x02:                                            /* Ctrl-B */
          do
            {
              --sel;
              if (sel < 0)
                sel = windows-1;
            } while (w[sel] == NULL);
          wm_cursor (w[sel]);
          disp ();
          break;
        case 0x04:                                            /* Ctrl-D */
          wm_close (w[sel]);
          wm_delete (w[sel]);
          w[sel] = NULL;
          n = 0;
          for (i = 0; i < windows; i++)
            if (w[i] != NULL)
              ++n;
          if (n > 0)
            next_window ();
          else
            c = 0x1b;
          break;
        case 0x05:                                            /* Ctrl-E */
          wm_puts (w[sel], buffer);
          break;
        case 0x06:                                            /* Ctrl-F */
          next_window ();
          break;
        case 0x08:                                            /* Ctrl-H */
          wm_backsp (w[sel], 1);
          break;
        case 0x09:                                            /* Ctrl-I */
          /* not yet implemented */
          break;
        case 0x0c:                                            /* Ctrl-L */
          wm_getxy (w[sel], &x, &y);
          wm_clr_eol (w[sel], x, y);
          break;
        case '\r':                                                /* CR */
          do_putc (sel, '\n');
          break;
        case 0x0e:                                            /* Ctrl-N */
          new_window ();
          break;
        case 0x12:                                            /* Ctrl-R */
          rnd_window ();
          break;
        case 0x1b:                                               /* Esc */
          break;
        default:
          if (c < 0x100 && c >= ' ')
            do_putc (sel, (char)c);
          else
            beep (880, 100);
          break;
        }
    } while (c != 0x1b);
  wm_exit ();
  return 0;
}
