/* winmgr1.c (emx+gcc) -- Copyright (c) 1987-1994 by Eberhard Mattes */

#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <sys/video.h>
#include <sys/winmgr.h>
#include "winmgr2.h"

wm_handle _wm_tab    = NULL;
wm_handle *_wm_idx   = NULL;

char * _wm_screen    = NULL;
char * _wm_line      = NULL;

int _wm_count        = 0;
int _wm_max          = 0;
int _wm_saved_cstart = 0;
int _wm_saved_cend   = 0;
int _wm_saved_x      = 0;
int _wm_saved_y      = 0;
int _wm_width        = 0;
int _wm_height       = 0;

wm_handle _wm_cursor = NULL;
int _wm_c_hide       = TRUE;


void _wm_cursor1 (void)
{
  int ax, ay;

  if (_wm_cursor != NULL)
    {
      ax = _wm_cursor->x + _wm_cursor->x0;
      ay = _wm_cursor->y + _wm_cursor->y0;
      if (ax < 0 || ax >= _wm_width || ay < 0 || ay >= _wm_height)
        v_hidecursor ();
      else if (!_wm_cursor->c_vis)
        v_hidecursor ();
      else if (_wm_cursor->open && (_wm_cursor->visible ||
                                    MASK (_wm_cursor,
                                          _wm_cursor->x + _wm_cursor->ax,
                                          _wm_cursor->y + _wm_cursor->ay)))
        {
          v_ctype (_wm_cursor->c_start, _wm_cursor->c_end);
          v_gotoxy (ax, ay);
        }
      else if (!_wm_c_hide)
        {
          v_ctype (_wm_saved_cstart, _wm_saved_cend);
          v_gotoxy (ax, ay);
        }
      else
        v_hidecursor ();
    }
  else
    v_hidecursor ();
}


void _wm_store1 (wm_handle wh, int x, int y, char c, int a)
{
  char *p;

  p = &DATA (wh, x, y);
  *p++ = c;
  *p = (char)a;
}



void _wm_clrline1 (wm_handle wh, int y, int x0, int x1)
{
  _wm_clrline2 (&DATA (wh, x0, y), x1-x0+1, (char)wh->wattr);
}



void _wm_line1 (wm_handle wh, int y)
{
  char *src;
  int yoffs;

  if (wh->open)
    {
      yoffs = y * wh->bwidth;
      src = &wh->data[2*(0+yoffs)];
      if (wh->visible)
        _wm_move2 (src, y+wh->by0, wh->bx0, wh->bx1, 1);
      else
        _wm_line2 (&wh->mask[0+yoffs], src, y+wh->by0, wh->bx0, wh->bx1);
    }
}


void _wm_allvisible (wm_handle wh)
{
  memset (wh->mask, TRUE, wh->masksize);
  wh->visible = TRUE;
}


void _wm_hide2 (wm_handle wh1, wm_handle wh2)
{
  int x0, xc, y, y0, y9;
  char *mask;

  wh1->visible = FALSE;
  x0 = MAX (wh1->bx0, wh2->bx0);
  xc = MIN (wh1->bx1, wh2->bx1) - x0 + 1;
  if (xc > 0)
    {
      y0 = MAX (wh1->by0, wh2->by0);
      y9 = MIN (wh1->by1, wh2->by1);
      mask = &MASK (wh1, x0 - wh1->bx0, y0 - wh1->by0);
      for (y = y0; y <= y9; y++)
        {
          memset (mask, FALSE, xc);
          mask += wh1->bwidth;
        }
    }
}


void _wm_hide1 (void)
{
  int i, j;
  wm_handle a, b;

  for (i = _wm_count-1; i >= 0; --i)
    _wm_allvisible (_wm_idx[i]);
  for (i = _wm_count-1; i >= 0; --i)
    {
      a = _wm_idx[i];
      if (a->open)
        for (j = i-1; j >= 0; j--)
          {
            b = _wm_idx[j];
            if (b->open && _wm_jam1 (a, b))
              _wm_hide2 (b, a);
          }
    }
}


void _wm_unhide1 (wm_handle wh, wm_handle new_wh)
{
  int ay, ry0, ay0, ay9, x0, x9, mx;
  int i;
  wm_handle p;
  char *src, *mask;

  _wm_allvisible (wh);
  for (i = 0; i < _wm_count; ++i)
    {
      p = _wm_idx[i];
      if (p != wh)
        {
          if (p->open && _wm_jam1 (wh, p))
            _wm_hide2 (wh, p);
        }
    }
  if (new_wh != NULL && _wm_jam1 (wh, new_wh))
    _wm_hide2 (wh, new_wh);
  x0 = wh->bx0; x9 = wh->bx1; mx = 0;
  ay0 = wh->by0; ay9 = wh->by1; ry0 = 0;
  if (x0 < 0)
    {
      mx = -x0;
      x0 = 0;
    }
  if (x9 >= _wm_width)
    x9 = _wm_width-1;
  if (ay0 < 0)
    {
      ry0 = -ay0;
      ay0 = 0;
    }
  if (ay9 >= _wm_height)
    ay9 = _wm_height-1;
  src = &_wm_screen[2*(x0+ay0*_wm_width)];
  mask = &MASK (wh, mx, ry0);
  for (ay = ay0; ay <= ay9; ++ay)
    {
      if (wh->visible)
        _wm_move2 (src, ay, x0, x9, 1);
      else
        {
          _wm_line2 (mask, src, ay, x0, x9);
          mask += wh->bwidth;
        }
      src += 2*_wm_width;
    }
}


void _wm_clr1 (wm_handle wh)
{
  int y;

  for (y = 0; y < wh->height; ++y)
    _wm_clrline1 (wh, y+wh->ay, wh->ax, wh->width-1+wh->ax);
}


void _wm_border1 (wm_handle wh, int title_flag, int title_attr,
                  const char *title)
{
  char ul, ur, ll, lr, ho, ve, tl, tr;
  int x2, y2;
  int i, attr;

  switch (wh->border)
    {
    case 0:
      return;
    case 1:
      ul = (char)'Ú'; ur = (char)'¿';
      ll = (char)'À'; lr = (char)'Ù';
      ho = (char)'Ä'; ve = (char)'³';
      tl = (char)'´'; tr = (char)'Ã';
      break;
    case 2:
      ul = (char)'É'; ur = (char)'»';
      ll = (char)'È'; lr = (char)'¼';
      ho = (char)'Í'; ve = (char)'º';
      tl = (char)'¹'; tr = (char)'Ì';
      break;
    case 3:
      ul = (char)0xd5; ur = (char)0xb8;
      ll = (char)0xd4; lr = (char)0xbe;
      ho = (char)0xcd; ve = (char)0xb3;
      tl = (char)0xb5; tr = (char)0xc6;
      break;
    case 4:
      ul = (char)0xd6; ur = (char)0xb7;
      ll = (char)0xd3; lr = (char)0xbd;
      ho = (char)0xc4; ve = (char)0xba;
      tl = (char)0xb6; tr = (char)0xc7;
      break;
    default:
      ul = ur = ll = lr = ho = ve = tl = tr = (char)wh->border;
    }
  attr = wh->battr;
  x2 = wh->x1+1 - wh->bx0;
  y2 = wh->y1+1 - wh->by0;
  _wm_store1 (wh, 0, 0, ul, attr);
  _wm_store1 (wh, 0, y2, ll, attr);
  for (i = wh->ay; i < y2; i++)
    _wm_store1 (wh, 0, i, ve, attr);
  _wm_store1 (wh, x2, 0, ur, attr);
  _wm_store1 (wh, x2, y2, lr, attr);
  for (i = wh->ay; i < y2; i++)
    _wm_store1 (wh, x2, i, ve, attr);
  for (i = wh->ax; i < x2; i++)
    _wm_store1 (wh, i, 0, ho, attr);
  for (i = wh->ax; i < x2; i++)
    _wm_store1 (wh, i, y2, ho, attr);
  if (title != NULL)
    {
      int tlen, twid, tx;
      
      tlen = strlen (title);
      if (title_flag == 0)
        twid = wh->bwidth-2;
      else
        {
          twid = wh->bwidth-4;
          if (tlen > twid)
            {
              twid += 2;
              title_flag = 0;
            }
        }
      if (tlen > twid)
        tlen = twid;
      tx = 1+(twid-tlen)/2;
      if (title_flag)
        {
          _wm_store1 (wh, tx       , 0, tl, attr);
          _wm_store1 (wh, tx+1+tlen, 0, tr, attr);
          ++tx;
        }
      for (i = 0; i < tlen; ++i)
        _wm_store1 (wh, tx++, 0, title[i], title_attr);
    }
}


void _wm_put1 (wm_handle wh)
{
  int y;

  if (wh->open)
    {
      for (y = 0; y < wh->bheight; ++y)
        _wm_line1 (wh, y);
      _wm_cursor1 ();
    }
  wh->update_req = FALSE;
}


int _wm_jam1 (wm_handle wh1, wm_handle wh2)
{
  return (wh1->bx0 <= wh2->bx1 &&
          wh2->bx0 <= wh1->bx1 &&
          wh1->by0 <= wh2->by1 &&
          wh2->by0 <= wh1->by1);
}


int _wm_idx1 (wm_handle wh)
{
  int i;

  for (i = 0; i < _wm_count; ++i)
    if (_wm_idx[i] == wh)
      return (i);
  return (-1);
}


void _wm_copy1 (wm_handle wh, wm_handle old_wh)
{
  int ay, x0, x9, xc, i, ry0, ay0, ay9, mx;
  wm_handle p;
  char *dst, *src, *mask;

  if (wh->open)
    {
      _wm_allvisible (wh);
      for (i = 0; i < _wm_count; ++i)
        {
          p = _wm_idx[i];
          if (p != wh)
            {
              if (p->open && _wm_jam1 (wh, p))
                _wm_hide2 (wh, p);
            }
        }
      if (old_wh != NULL && _wm_jam1 (wh, old_wh))
        _wm_hide2 (wh, old_wh);
      x0 = wh->bx0; x9 = wh->bx1; mx = 0;
      ay0 = wh->by0; ay9 = wh->by1; ry0 = 0;
      if (x0 < 0)
        {
          mx = -x0;
          x0 = 0;
        }
      if (x9 >= _wm_width)
        x9 = _wm_width-1;
      if (ay0 < 0)
        {
          ry0 = -ay0;
          ay0 = 0;
        }
      if (ay9 >= _wm_height)
        ay9 = _wm_height-1;
      xc = x9-x0+1;
      if (xc > 0)
        {
          src = &_wm_line[2*x0];
          dst = &_wm_screen[2*(x0+ay0*_wm_width)];
          mask = &MASK (wh, mx, ry0);
          for (ay = ay0; ay <= ay9; ay++)
            {
              _wm_move2 (_wm_line, ay, 0, _wm_width-1, 0);
              if (wh->visible)
                memmove (dst, src, 2*xc);
              else
                {
                  _wm_copy2 (mask, dst, src, xc);
                  mask += wh->bwidth;
                }
              dst += 2*_wm_width;
            }
        }
    }
}


int wm_init (int n)
{
  int i;

  if (n <= 0)
    return (FALSE);
  if (!v_init ())
    return (FALSE);
  v_dimen (&_wm_width, &_wm_height);
  v_getctype (&_wm_saved_cstart, &_wm_saved_cend);
  v_ctype (-1, -1);
  v_getxy (&_wm_saved_x, &_wm_saved_y);
  _wm_cursor = NULL;
  _wm_c_hide = TRUE;
  _wm_count = 0;
  _wm_tab = calloc (n, sizeof (struct _wm_window));
  _wm_idx = calloc (n, sizeof (wm_handle));
  _wm_screen = _tmalloc (2 * _wm_width * _wm_height);
  _wm_line = _tmalloc (2*_wm_width);
  if (_wm_tab == NULL || _wm_idx == NULL || _wm_screen == NULL ||
      _wm_line == NULL)
    {
      if (_wm_tab != NULL)
        {
          free (_wm_tab); _wm_tab = NULL;
        }
      if (_wm_idx != NULL)
        {
          free (_wm_idx); _wm_idx = NULL;
        }
      if (_wm_screen != NULL)
        {
          _tfree (_wm_screen); _wm_screen = NULL;
        }
      if (_wm_line != NULL)
        {
          _tfree (_wm_line); _wm_line = NULL;
        }
      _wm_max = 0;
      return (FALSE);
    }
  else
    {
      _wm_max = n;
      for (i = 0; i < _wm_max; ++i)
        _wm_tab[i].used = 0;
      return (TRUE);
    }
}


wm_handle wm_create (int x0, int y0, int x1, int y1, int border, int battr,
                     int wattr)
{
  int i, bx0, by0, bx1, by1;
  long ldatasize;
  unsigned datasize;
  wm_handle wh;

  if (_wm_tab == NULL || x1 < x0 || y1 < y0 || x1-x0 > 1000 || y1-y0 > 1000)
    return (NULL);
  for (i = 0; i < _wm_max; ++i)
    if (_wm_tab[i].used != WM_USED)
      break;
  if (i >= _wm_max)
    return (NULL);                                     /* No slot found */
  wh = &_wm_tab[i];
  wh->x0 = x0; wh->y0 = y0;
  wh->x1 = x1; wh->y1 = y1;
  bx0 = x0; by0 = y0;
  bx1 = x1; by1 = y1;
  if (border != 0)
    {
      --bx0; --by0;
      ++bx1; ++by1;
    }
  wh->bx0 = bx0; wh->by0 = by0;
  wh->bx1 = bx1; wh->by1 = by1;
  wh->width = x1-x0+1; wh->bwidth = bx1-bx0+1;
  wh->height = y1-y0+1; wh->bheight = by1-by0+1;
  wh->ax = x0-bx0; wh->ay = y0-by0;
  ldatasize = (long)wh->bwidth * (long)wh->bheight;
#if !defined (__EMX__)
  if (ldatasize >= 32768L)
    return (NULL);
#endif
  datasize = (unsigned)ldatasize;
  wh->data = _tmalloc (2*datasize);
  wh->mask = malloc (datasize);
  if (wh->data == NULL || wh->mask == NULL)
    {
      if (wh->data != NULL) _tfree (wh->data);
      if (wh->mask != NULL) free (wh->mask);
      return (NULL);                                     /* Out of memory */
    }
  wh->masksize = datasize;
  wh->border = border;
  wh->battr = battr; wh->wattr = wattr;
  wh->x = 0; wh->y = 0;
  wh->used = WM_USED; wh->open = FALSE; wh->display = FALSE;
  wh->update_flag = TRUE; wh->update_req = FALSE;
  wh->wrap = TRUE;
  wh->c_vis = TRUE;
  wh->c_start = _wm_saved_cstart; wh->c_end = _wm_saved_cend;
  _wm_idx[_wm_count++] = wh;
  wh->visible = TRUE;
  _wm_clr1 (wh);
  _wm_border1 (wh, 0, 0, NULL);
  _wm_hide1 ();
  _wm_cursor1 ();
  return (wh);
}


void wm_attrib (wm_handle wh, int a)
{
  if (wh->used != WM_USED)
    return;
  wh->wattr = a;
}


void wm_cursor (wm_handle wh)
{
  if (wh != NULL && wh->used != WM_USED)
    return;
  _wm_cursor = wh;
  _wm_cursor1 ();
}


void wm_chide (int flag)
{
  _wm_c_hide = flag;
  _wm_cursor1 ();
}


void wm_cvis (wm_handle wh, int flag)
{
  if (wh != NULL && wh->used != WM_USED)
    return;
  wh->c_vis = flag;
  _wm_cursor1 ();
}


wm_handle wm_get_cursor (void)
{
  return (_wm_cursor);
}
