#ifdef _plan9_
#include <u.h>
#include <libc.h>
#else
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#endif
#include <fconfig.h>
#include <filter.h>
#include <ui_helper.h>
#include <archaccel.h>
#include "grlib.h"

/* This is quite simple and ugly implementation of windows.
 * it redraws all windows every frame and removes them before calculation
 * it should be OK for small windows (like texts) used by ui_helper
 * I plan to abuse it in ugly interface too, in case it will be fast enought
 */

static void
uih_darkrectangle (struct image *image, int x, int y, int width, int height)
{

  int mask = 0;
  unsigned int *current, *end;
  if (x + width < 0 || y + height < 0 || y >= image->height
      || x >= image->width)
    return;
  if (x + width >= image->width)
    width = image->width - x;
  if (x < 0)
    width += x, x = 0;
  if (width <= 0)
    return;
  if (y + height >= image->height)
    height = image->height - y;
  if (y < 0)
    height += y, y = 0;
  if (height <= 0)
    return;
  assert (x >= 0 && y >= 00 && width > 0 && height > 0
	  && x + width <= image->width && y + height <= image->height);
  if (image->bytesperpixel == 2)
    {
      int x1 = x / 2;
      width = (x + width + 1) / 2 - x1;
      x = x1;
    }
  if (image->bytesperpixel == 3)
    {
      int x1 = x * 3 / 4;
      width = (x + width + 2) * 3 / 4 - x1;
      x = x1;
    }
  switch (image->palette->type)
    {
    case TRUECOLOR:
    case TRUECOLOR24:
      mask =
	~((1 <<
	   (image->palette->info.truec.rshift + 7 -
	    image->palette->info.truec.rprec)) | (1 << (image->palette->
							info.truec.gshift +
							7 -
							image->palette->
							info.truec.gprec)) |
	  (1 <<
	   (image->palette->info.truec.bshift + 7 -
	    image->palette->info.truec.bprec)));
      break;
    case TRUECOLOR16:
      mask =
	((1 <<
	  (image->palette->info.truec.rshift + 7 -
	   image->palette->info.truec.rprec)) | (1 << (image->palette->
						       info.truec.gshift + 7 -
						       image->palette->
						       info.truec.gprec)) | (1
									     <<
									     (image->
									      palette->
									      info.
									      truec.
									      bshift
									      +
									      7
									      -
									      image->
									      palette->
									      info.
									      truec.
									      bprec)));
      mask = ~(mask | (mask << 16));
      break;
    }
  height += y;
  while (y < height)
    {
      current = ((unsigned int *) image->currlines[y]) + x;
      end = current + width;
      while (current < end)
	*current = (*current >> 1) & mask, current++;
      y++;
    }

}
void
uih_drawborder (struct uih_context *uih, int x, int y, int width, int height,
		int flags)
{
  int leftcolor;
  int rightcolor;
  int bgcolor;
  if (uih->palette->type & BITMAPS)
    {
      if (flags & BORDER_PRESSED)
	{
	  bgcolor = FGCOLOR (uih);
	  rightcolor = BGCOLOR (uih);
	  leftcolor = BGCOLOR (uih);
	}
      else
	{
	  bgcolor = BGCOLOR (uih);
	  rightcolor = FGCOLOR (uih);
	  leftcolor = FGCOLOR (uih);
	}
    }
  else
    {
      if (flags & BORDER_LIGHT)
	{
	  bgcolor = LIGHTGRAYCOLOR (uih);
	  rightcolor = BGCOLOR (uih);
	  leftcolor = LIGHTGRAYCOLOR2 (uih);
	}
      else
	{
	  bgcolor = DARKGRAYCOLOR (uih);
	  rightcolor = BGCOLOR (uih);
	  leftcolor = LIGHTGRAYCOLOR (uih);
	}
      if (flags & BORDER_PRESSED)
	{
	  int i = leftcolor;
	  leftcolor = rightcolor;
	  rightcolor = i;
	}
      if (uih->image->flags & AAIMAGE)
	bgcolor = BGCOLOR (uih);
    }
  if (uih->image->bytesperpixel > 1 && (flags & BORDER_TRANSPARENT))
    uih_darkrectangle (uih->image, x + 1, y + 1, width - 2,
		       height - 2) /*, leftcolor = GRAYCOLOR (uih) */ ;
  else
    {
      xrectangle (uih->image, x + 1, y + 1, width - 2, height - 2, bgcolor);
    }
  xhline (uih->image, x, y, width - 1, leftcolor);
  xhline (uih->image, x, y + height - 1, width - 1, rightcolor);
  xvline (uih->image, x, y, height - 1, leftcolor);
  xvline (uih->image, x + width - 1, y, height - 1, rightcolor);
}

struct uih_window *
uih_registerw (struct uih_context *uih, uih_getposfunc
	       getpos, uih_drawfunc draw, void *data, int flags)
{
  struct uih_window *w =
    (struct uih_window *) calloc (1, sizeof (struct uih_window));
  struct uih_window *w1;
  assert (uih != NULL && getpos != NULL && draw != NULL && flags >= 0);
  if (w == NULL)
    return NULL;
  uih_clearwindows (uih);
  w1 = uih->wtop;
  w->getpos = getpos;
  w->draw = draw;
  w->data = data;
  w->flags = flags;
  w->savedline = -1;
  w->saveddata = NULL;
  w->flags |= BORDER_TRANSPARENT | BORDER_PRESSED | BORDER_LIGHT;
  w->next = NULL;
  if (w1 == NULL)
    {
      uih->wtop = w;
    }
  else
    {
      while (w1->next != NULL)
	w1 = w1->next;
      w1->next = w;
    }
  w->previous = w1;
  w->x = -65536;
  return w;
}

void
uih_setline (struct uih_context *uih, struct uih_window *w, int color, int x1,
	     int y1, int x2, int y2)
{
  if (w->savedline != color || w->x != x1 || w->y != y1 || w->width != x2 - x1
      || w->height != y2 - y1)
    {
      uih_clearwindows (uih);
      uih->display = 1;
      w->savedline = color;
      w->x = x1;
      w->y = y1;
      w->width = x2 - x1;
      w->height = y2 - y1;
    }
}
struct uih_window *
uih_registerline (struct uih_context *uih, int color, int x1, int y1, int x2,
		  int y2)
{
  struct uih_window *w =
    (struct uih_window *) calloc (1, sizeof (struct uih_window));
  struct uih_window *w1;
  if (w == NULL)
    return NULL;
  uih_clearwindows (uih);
  w1 = uih->wtop;
  uih->display = 1;
  w->getpos = NULL;
  w->savedline = color;
  w->flags = 0;
  w->x = x1;
  w->y = y1;
  w->width = x2 - x1;
  w->height = y2 - y1;
  w->saveddata = NULL;
  w->next = NULL;
  if (w1 == NULL)
    {
      uih->wtop = w;
    }
  else
    {
      while (w1->next != NULL)
	w1 = w1->next;
      w1->next = w;
    }
  w->previous = w1;
  return w;
}

void
uih_removew (struct uih_context *uih, struct uih_window *w)
{
  uih_clearwindows (uih);
  assert (uih->wtop != NULL);
  assert (w != NULL);
  uih->display = 1;

  if (w->previous == NULL)
    {
      assert (uih->wtop == w);
      uih->wtop = w->next;
    }
  else
    {
      w->previous->next = w->next;
    }
  if (w->next != NULL)
    {
      w->next->previous = w->previous;
    }
  free (w);
}

/*Remove all drawed windows from screen */
void
uih_clearwindows (struct uih_context *uih)
{
  struct uih_window *w = uih->wtop;
  int savedline = 0;
  int savedpos = 0;
  int destwidth = uih->image->width * uih->image->bytesperpixel;
  if (!uih->wdisplayed)
    return;
  if (!uih->image->bytesperpixel)
    {
      destwidth = (w->x + uih->image->width + 7) / 8;
    }
  uih->wdisplayed = 0;
  if (uih->wflipped)
    uih->image->flip (uih->image), uih->wflipped = 0;
  while (w)
    {
      if (w->getpos == NULL)
	{
	  if (w->saveddata != NULL)
	    {
	      xrestoreline (uih->image, w->saveddata, w->x, w->y,
			    w->width + w->x, w->height + w->y);
	      free (w->saveddata);
	      w->saveddata = NULL;
	    }
	}
      else
	{
	  if (w->savedline != -1 || w->saveddata != NULL)
	    {
	      int i;
	      int xskip = w->x * uih->image->bytesperpixel;
	      int width = w->width * uih->image->bytesperpixel;
	      if (!uih->image->bytesperpixel)
		{
		  xskip = w->x / 8;
		  width = (w->x + w->width + 7) / 8 - xskip;
		}
	      assert (w->width);
	      assert (w->height);
	      assert (w->x >= 0);
	      assert (w->y >= 0);
	      assert (w->x + w->width <= uih->image->width);
	      assert (w->y + w->height <= uih->image->height);
	      if (w->savedline != -1)
		{
		  savedline = w->savedline;
		  savedpos = w->savedpos;
		  for (i = w->y; i < w->y + w->height; i++)
		    {
		      unsigned char *data = uih->image->currlines[i] + xskip;
		      assert (savedline < uih->image->height);
		      assert (savedline >= 0);
		      assert (savedpos >= 0 && savedpos <= destwidth);
		      if (width + savedpos > destwidth)
			{
			  int width1;
			  memcpy (data,
				  uih->image->oldlines[savedline] + savedpos,
				  destwidth - savedpos);
			  savedline++;
			  width1 = width - destwidth + savedpos;
			  memcpy (data + (destwidth - savedpos),
				  uih->image->oldlines[savedline], width1);
			  savedpos = width1;
			}
		      else
			memcpy (data,
				uih->image->oldlines[savedline] + savedpos,
				width), savedpos += width;
		    }
		  w->savedline = -1;
		}
	      else
		{
		  assert (w->saveddata);
		  for (i = w->y; i < w->y + w->height; i++)
		    {
		      unsigned char *data = uih->image->currlines[i] + xskip;
		      memcpy (data, w->saveddata + (i - w->y) * width, width);
		    }
		  free (w->saveddata);
		  w->saveddata = NULL;
		}
	    }
	}
      w = w->next;
    }
}
void
uih_drawwindows (struct uih_context *uih)
{
  struct uih_window *w = uih->wtop;
  struct image *img = uih->image;
  int size = 0;
  int nocopy = 0;
  int savedline = 0;
  int savedpos = 0;
  int destwidth = uih->image->width * uih->image->bytesperpixel;
  if (!uih->image->bytesperpixel)
    {
      destwidth = (w->x + uih->image->width + 7) / 8;
    }
  if (uih->wdisplayed)
    return;
  uih->wdisplayed = 1;
  while (w)
    {
      if (w->getpos != NULL)
	{
	  int test = w->x == -65536;
	  w->getpos (uih, &w->x, &w->y, &w->width, &w->height, w->data);
	  if (w->x < 0)
	    w->width -= w->x, w->x = 0;
	  if (w->y < 0)
	    w->height -= w->y, w->y = 0;
	  if (w->x > img->width)
	    w->width = 0, w->height = 0, w->x = 0;
	  if (w->y > img->height)
	    w->width = 0, w->height = 0, w->y = 0;
	  if (w->x + w->width > img->width)
	    w->width = img->width - w->x;
	  if (w->y + w->height > img->height)
	    w->height = img->height - w->y;
	  if (w->width < 0)
	    w->width = 0;
	  if (w->height < 0)
	    w->height = 0;
	  if (test)
	    {
	      struct uih_window *w1 = uih->wtop;
	      while (w1)
		{
		  if (w != w1 && (w1->flags & DRAWBORDER) &&
		      ((((w1->x > w->x + 5 && w1->x + 5 < w->x + w->width) ||
			 (w->x > w1->x + 5 && w->x + 5 < w1->x + w1->width))
			&& ((w1->y > w->y + 5 && w1->y + 5 < w->y + w->height)
			    || (w->y > w1->y + 5
				&& w->y + 5 < w1->y + w1->height)))
		       ||
		       (((w1->x + w1->width > w->x + 5
			  && w1->x + w1->width + 5 < w->x + w->width)
			 || (w->x + w->width > w1->x + 5
			     && w->x + w->width + 5 < w1->x + w1->width))
			&&
			((w1->y + w1->height > w->y + 5
			  && w1->y + w1->height + 5 < w->y + w->height)
			 || (w->y + w->height > w1->y + 5
			     && w->y + w->height + 5 < w1->y + w1->height)))))
		    {
		      w->flags &= ~BORDER_TRANSPARENT;
		      break;
		    }
		  w1 = w1->next;
		}
	    }
	  size += w->width * w->height;
	  if (w->x == 0 && w->y == 0 && w->width == img->width
	      && w->height == img->height)
	    nocopy = 1;
	  assert (w->width >= 0);
	  assert (w->height >= 0);
	  assert (w->x >= 0);
	  assert (w->y >= 0);
	  assert (w->x + w->width <= uih->image->width);
	  assert (w->y + w->height <= uih->image->height);
	}
      w = w->next;
    }
  if (size > img->width * img->height / 2)
    {
      int i;
      int width = img->width * img->bytesperpixel;
      if (!width)
	width = (img->width + 7) / 8;
      uih->wflipped = 1;
      if (!nocopy)
	for (i = 0; i < img->height; i++)
	  memcpy (img->oldlines[i], img->currlines[i], width);
      uih->image->flip (uih->image);
    }
  else
    {
      int savedminx = -1;
      int savedmaxx = -1;
      int savedminy = -1;
      int savedmaxy = -1;
      uih->wflipped = 0;
      w = uih->wtop;
      while (w)
	{
	  int i;
	  assert (w->saveddata == NULL);
	  if (w->getpos == NULL)
	    {
	      if ((w->x < savedminx || w->y < savedminy
		   || w->x + w->width > savedmaxx
		   || w->x + w->height > savedmaxy
		   || w->x + w->width < savedminx
		   || w->y + w->height < savedminy || w->x > savedmaxx
		   || w->y > savedmaxy))
		{
		  w->saveddata =
		    xsaveline (uih->image, w->x, w->y, w->width + w->x,
			       w->height + w->y);
		}
	    }
	  else
	    {
	      assert (w->savedline == -1);
	      if (w->width && w->height
		  && (w->x < savedminx || w->y < savedminy
		      || w->x + w->width > savedmaxx
		      || w->y + w->height > savedmaxy))
		{
		  int xskip = w->x * uih->image->bytesperpixel;
		  int width = w->width * uih->image->bytesperpixel;
		  savedminx = w->x;
		  savedminy = w->y;
		  savedmaxx = w->x + w->width;
		  savedmaxy = w->y + w->height;
		  if (!uih->image->bytesperpixel)
		    {
		      xskip = w->x / 8;
		      width = (w->x + w->width + 7) / 8 - xskip;
		    }
		  if (uih->image->flags & PROTECTBUFFERS || 1)
		    {
		      w->saveddata = (char *) malloc (width * w->height + 1);
		      if (w->saveddata != NULL)
			for (i = w->y; i < w->y + w->height; i++)
			  {
			    unsigned char *data = img->currlines[i] + xskip;
			    memcpy (w->saveddata + (i - w->y) * width, data,
				    width);
			  }

		    }
		  else
		    {
		      w->savedline = savedline;
		      w->savedpos = savedpos;
		      for (i = w->y; i < w->y + w->height; i++)
			{
			  unsigned char *data = img->currlines[i] + xskip;
			  if (width + savedpos > destwidth)
			    {
			      int width1;
			      memcpy (uih->image->oldlines[savedline] +
				      savedpos, data, destwidth - savedpos);
			      savedline++;
			      width1 = width - destwidth + savedpos;
			      memcpy (uih->image->oldlines[savedline],
				      data + (destwidth - savedpos), width1);
			      savedpos = width1;
			    }
			  else
			    memcpy (uih->image->oldlines[savedline] +
				    savedpos, data, width), savedpos += width;
			}
		    }
		}
	    }
	  w = w->next;
	}
    }
  w = uih->wtop;
  while (w)
    {
      if (w->getpos == NULL)
        {
// This code unfortunately won't work for the Julia show, so
// deactivated:
#if 0
	  int lw=0, lwi=0, lwj=0;
	  if (uih->image->width > 800 && uih->image->height > 600)
	    lw=3;

	  for (; lwi <= lw; ++lwi)
	    for (; lwj <= lw; ++lwj)
#endif
#define lwi 0
#define lwj 0
	      xline (uih->image, w->x + lwi, w->y + lwj, w->width + w->x + lwi,
	        w->height + w->y + lwj, w->savedline);
        }
      else if (w->width && w->height)
	{
	  if (w->flags & DRAWBORDER)
	    uih_drawborder (uih, w->x, w->y, w->width, w->height,
			    (BORDER_TRANSPARENT) & w->flags);
	  w->draw (uih, w->data);
	}
      w = w->next;
    }
}
