#ifndef _plan9_
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


#include <config.h>
#include <filter.h>
#include <fractal.h>
#include <ui_helper.h>
#include <xmenu.h>
#include <grlib.h>
#include <ui.h>
#include <xerror.h>
#include <misc-f.h>
#include "uiint.h"

#ifdef HAVE_GETTEXT
#include <libintl.h>
#else
#define gettext(STRING) STRING
#endif

static char **dirs;
static char **sdirs;
static int ndirs;
static char **names;
static char **snames;
static int nnames;

struct ui_textdata *dir, *filename;
static char lastdir[256];
static char *currdir;

static struct uih_window *filew;
static CONST char *mask;
static void (*callback) (CONST char *name, int succ);
int filevisible;
static int filex, filey, filewidth, fileheight;

#define ADIR 0
#define AFILELIST 1
#define ADIRLIST 2
#define AFILE 3
#define AOK 4

#define DIRSTART (filey+BORDERHEIGHT)
#define LISTSTART (DIRSTART+BUTTONHEIGHT+BORDERHEIGHT)
#define LISTEND (FILESTART-BORDERHEIGHT)
#define FILESTART (OKSTART-BORDERHEIGHT-BUTTONHEIGHT)
#define OKSTART (filey+fileheight-BORDERHEIGHT-BUTTONHEIGHT)
#define SCROLLWIDTH 10

#define LISTWIDTH ((filewidth-10*BORDERWIDTH-2*SCROLLWIDTH)/2)

#define NVISIBLE ((LISTEND-LISTSTART-2*BORDERHEIGHT)/xtextheight(uih->font))

static int selectedname;
static int selecteddir;
static int namestart;
static int dirstart;

static int pressedbutton;
static int activebutton;
static int active;

static void
ui_freenames (void)
{
  int i;
  selectedname = 0;
  selecteddir = 0;
  if (nnames)
    {
      for (i = 0; i < nnames; i++)
	free (names[i]), free (snames[i]);
      free (names);
      nnames = 0;
    }
  if (ndirs)
    {
      for (i = 0; i < ndirs; i++)
	free (dirs[i]), free (sdirs[i]);
      free (dirs);
      ndirs = 0;
    }
  if (snames)
    free (snames), snames = NULL;
  if (sdirs)
    free (sdirs), sdirs = NULL;
}
static int
compar (CONST void *a, CONST void *b)
{
  return (strcmp (*(CONST char **) a, *(CONST char **) b));
}
static char **
ui_mksnames (int nnames, char **names, int width)
{
  char **snames = NULL;
  int i;
  if (nnames)
    {
      qsort (names, nnames, sizeof (*names),	/*(int (*)(CONST void *, CONST
						   void *))strcmp */ compar);
      snames = (char **) malloc (sizeof (*snames) * nnames);
      for (i = 0; i < nnames; i++)
	{
	  if (xtextwidth (uih->font, names[i]) <= width)
	    snames[i] = mystrdup (names[i]);
	  else
	    {
	      int y;
	      int swidth = 0;
	      int len = (int) strlen (names[i]);
	      snames[i] = (char *) malloc (strlen (names[i]) + 2);
	      for (y = len - 4; y < len; y++)
		swidth += xtextcharw (uih->font, names[i][y]);
	      swidth += xtextcharw (uih->font, '|');
	      y = 0;
	      while (swidth < width)
		{
		  snames[i][y] = names[i][y];
		  swidth += xtextcharw (uih->font, names[i][y]);
		  y++;
		}
	      snames[i][y - 1] = '|';
	      snames[i][y] = 0;
	      strcat (snames[i], names[i] + len - 4);
	    }
	}
    }
  return (snames);
}
static void
ui_buildnames (int width)
{
  ui_freenames ();
  xio_getfiles (currdir, &names, &dirs, &nnames, &ndirs);
  if (snames)
    free (snames), snames = NULL;
  if (sdirs)
    free (sdirs), sdirs = NULL;
  snames = ui_mksnames (nnames, names, width);
  sdirs = ui_mksnames (ndirs, dirs, width);
}

void
ui_closefilesel (int success)
{
  char *text =
    (char *) malloc ((int) strlen (filename->text) + (int) strlen (currdir) +
		     3);
  filevisible = 0;
  uih_removew (uih, filew);
  ui_freenames ();
  sprintf (text, "%s" XIO_PATHSEPSTR "%s", currdir, filename->text);
  ui_closetext (dir);
  ui_closetext (filename);
  strcpy (lastdir, currdir);
  free (currdir);
  callback (text, success);
  free (text);
  uih->display = 1;
  ui_freenames ();
}
static void
filepos (uih_context * c, int *x, int *y, int *w, int *h, void *data)
{
  *x = filex;
  *y = filey;
  *w = filewidth;
  *h = fileheight;
}
static void
drawfile (uih_context * c, void *data)
{
  int i;
  int ypos;
  int h = xtextheight (uih->font);
  uih_drawborder (uih, filex + BORDERWIDTH, DIRSTART,
		  filewidth - 2 * BORDERWIDTH, BUTTONHEIGHT,
		  BORDER_PRESSED | BORDER_LIGHT);
  uih_drawborder (uih, filex + BORDERWIDTH, FILESTART,
		  filewidth - 2 * BORDERWIDTH, BUTTONHEIGHT,
		  BORDER_PRESSED | BORDER_LIGHT);

  ui_drawbutton ("OK", (pressedbutton == 0), active == AOK
		 && activebutton == 0, filex + BORDERWIDTH,
		 filex + filewidth / 2 - BORDERWIDTH, OKSTART);
  ui_drawbutton (gettext ("Cancel"), (pressedbutton == 1), active == AOK
		 && activebutton == 1, filex + filewidth / 2 + BORDERWIDTH,
		 filex + filewidth - BORDERWIDTH, OKSTART);

  uih_drawborder (uih, filex + BORDERWIDTH, LISTSTART,
		  LISTWIDTH + 3 * BORDERWIDTH + SCROLLWIDTH,
		  LISTEND - LISTSTART, BORDER_PRESSED);
  uih_drawborder (uih, filex + filewidth / 2 + BORDERWIDTH, LISTSTART,
		  LISTWIDTH + 3 * BORDERWIDTH + SCROLLWIDTH,
		  LISTEND - LISTSTART, BORDER_PRESSED);
  ypos = LISTSTART + BORDERHEIGHT;
  for (i = 0; ypos + h < LISTEND && i + namestart < nnames; i++)
    {
      if (i + namestart == selectedname)
	{
	  xrectangle (uih->image, filex + 2 * BORDERWIDTH, ypos, LISTWIDTH, h,
		      (uih->palette->type & BITMAPS) ? BGCOLOR (uih) :
		      LIGHTGRAYCOLOR (uih));
	}
      if (uih->palette->type & BITMAPS)
	xprint (uih->image, uih->font, filex + 2 * BORDERWIDTH, ypos,
		snames[i + namestart], uih->encoding,
		i + namestart == selectedname ? FGCOLOR (uih) : BGCOLOR (uih),
		BGCOLOR (uih), TEXT_PRESSED);
      else
	xprint (uih->image, uih->font, filex + 2 * BORDERWIDTH, ypos,
		snames[i + namestart], uih->encoding, (i + namestart) == selectedname
		&& active == AFILELIST ? SELCOLOR (uih) : FGCOLOR (uih),
		BGCOLOR (uih), 0);
      ypos += h;
    }
  if (nnames)
    {
      int xstart = (namestart) * (LISTEND - LISTSTART) / nnames;
      int xend = (namestart + NVISIBLE) * (LISTEND - LISTSTART) / nnames;
      if (xstart > (LISTEND - LISTSTART - 2 * BORDERHEIGHT))
	xstart = LISTEND - LISTSTART - 2 * BORDERHEIGHT;
      if (xend > (LISTEND - LISTSTART - 2 * BORDERHEIGHT))
	xend = LISTEND - LISTSTART - 2 * BORDERHEIGHT;
      uih_drawborder (uih, filex + LISTWIDTH + 3 * BORDERWIDTH, LISTSTART + xstart + BORDERHEIGHT, SCROLLWIDTH, xend - xstart,	/*1|BORDER_LIGHT */
		      0);
    }

  ypos = LISTSTART + BORDERHEIGHT;
  for (i = 0; ypos + h < LISTEND && i + dirstart < ndirs; i++)
    {
      if (i + dirstart == selecteddir)
	{
	  xrectangle (uih->image, filex + filewidth / 2 + 2 * BORDERWIDTH,
		      ypos, LISTWIDTH, h,
		      (uih->palette->type & BITMAPS) ? BGCOLOR (uih) :
		      LIGHTGRAYCOLOR (uih));
	}
      if (uih->palette->type & BITMAPS)
	xprint (uih->image, uih->font,
		filex + filewidth / 2 + 2 * BORDERWIDTH, ypos,
		sdirs[i + dirstart], uih->encoding,
		i + dirstart == selecteddir ? FGCOLOR (uih) : BGCOLOR (uih),
		BGCOLOR (uih), TEXT_PRESSED);
      else
	xprint (uih->image, uih->font,
		filex + filewidth / 2 + 2 * BORDERWIDTH, ypos,
		sdirs[i + dirstart], uih->encoding, (i + dirstart) == selecteddir
		&& active == ADIRLIST ? SELCOLOR (uih) : FGCOLOR (uih),
		BGCOLOR (uih), 0);
      ypos += h;
    }
  if (ndirs)
    {
      int xstart = (dirstart) * (LISTEND - LISTSTART) / ndirs;
      int xend = (dirstart + NVISIBLE) * (LISTEND - LISTSTART) / ndirs;
      if (xstart > (LISTEND - LISTSTART - 2 * BORDERHEIGHT))
	xstart = LISTEND - LISTSTART - 2 * BORDERHEIGHT;
      if (xend > (LISTEND - LISTSTART - 2 * BORDERHEIGHT))
	xend = LISTEND - LISTSTART - 2 * BORDERHEIGHT;
      uih_drawborder (uih, filex + filewidth / 2 + LISTWIDTH + 3 * BORDERWIDTH, LISTSTART + xstart + BORDERHEIGHT, SCROLLWIDTH, xend - xstart,	/*1|BORDER_LIGHT */
		      0);
    }
  ui_drawtext (filename, active == AFILE);
  ui_drawtext (dir, active == ADIR);
}

static void
setname (int name)
{
  ui_closetext (filename);
  filename =
    ui_opentext (filex + 2 * BORDERWIDTH, FILESTART + BORDERHEIGHT,
		 filewidth - 4 * BORDERWIDTH, names[name]);
}

#ifdef _WIN32
#define DRIVES
#endif
#ifdef DJGPP
#define DRIVES
#endif
static void
setdir (int name)
{
  char *dirstring = dirs[name];
  char *s = NULL;
  if (dirstring[0] == '.' && !dirstring[1])
    {
      /*do nothing */
      s = mystrdup (currdir);
    }
  else if (dirstring[0] && dirstring[1] && dirstring[0] == '.'
	   && dirstring[1] == '.' && !dirstring[2])
    {
      int i = (int) strlen (currdir);
      s =
	(char *) malloc ((int) strlen (dirstring) + (int) strlen (currdir) +
			 2);
      strcpy (s, currdir);
      for (; i >= 0 && s[i] != '/' && s[i] != '\\' && s[i] != XIO_PATHSEP;
	   i--);
      if (i < 0)
	free (s), s = NULL;
      else
	s[i] = 0;
    }
  if (s == NULL)
    {
      int i = (int) strlen (currdir);
      s =
	(char *) malloc ((int) strlen (dirstring) + (int) strlen (currdir) +
			 2);
      strcpy (s, currdir);
      if (currdir[i - 1] != '/' && currdir[i - 1] != '\\'
	  && currdir[i - 1] != XIO_PATHSEP)
	strcat (s, XIO_PATHSEPSTR);
      strcat (s, dirstring);
      if (!s[0])
	s[0] = XIO_PATHSEP, s[1] = 0;
    }
  free (currdir);
#ifdef DRIVES
  if (strlen (s) == 2
      && ((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z'))
      && s[1] == ':')
    s[2] = XIO_PATHSEP, s[3] = 0;
#endif
  currdir = s;
  ui_closetext (dir);
  dir =
    ui_opentext (filex + 2 * BORDERWIDTH, DIRSTART + BORDERHEIGHT,
		 filewidth - 4 * BORDERWIDTH, currdir);
  ui_freenames ();
  ui_buildnames (LISTWIDTH);
  dirstart = 0;
  selecteddir = 0;
  namestart = 0;
  selectedname = 0;
  uih->display = 1;
}
static void
setexactdir (CONST char *dirstring)
{
  free (currdir);
  currdir = mystrdup (dirstring);
  ui_closetext (dir);
  dir =
    ui_opentext (filex + 2 * BORDERWIDTH, DIRSTART + BORDERHEIGHT,
		 filewidth - 4 * BORDERWIDTH, currdir);
  ui_freenames ();
  ui_buildnames (LISTWIDTH);
  dirstart = 0;
  selecteddir = 0;
  namestart = 0;
  selectedname = 0;
  uih->display = 1;
}

int
ui_keyfilesel (int k)
{
  if (!filevisible)
    return 0;
  if (k == UIKEY_ESC)
    ui_closefilesel (0);
  else
    switch (active)
      {
      default:
	active = AFILE;
	uih->display = 1;
	break;
      case ADIR:
	if (ui_textkey (dir, k))
	  break;
	if (k == '\n' || k == 13)
	  {
	    active = AFILE;
	    setexactdir (dir->text);
	    uih->display = 1;
	  }
	if (k == UIKEY_UP)
	  {
	    active = AOK;
	    uih->display = 1;
	    activebutton = 1;
	  }
	if (k == '\t' || k == UIKEY_DOWN)
	  {
	    active++;
	    uih->display = 1;
	  }
	break;
      case AFILELIST:
	switch (k)
	  {
	  case '\t':
	  case UIKEY_RIGHT:
	    uih->display = 1;
	    active = ADIRLIST;
	    break;
	  case UIKEY_LEFT:
	    uih->display = 1;
	    active = ADIR;
	    break;
	  case UIKEY_UP:
	    if (selectedname)
	      {
		uih->display = 1;
		selectedname--;
		if (selectedname < namestart)
		  namestart = selectedname;
	      }
	    break;
	  case UIKEY_DOWN:
	    if (selectedname < nnames - 1)
	      {
		uih->display = 1;
		selectedname++;
		if (selectedname >= namestart + NVISIBLE)
		  namestart = selectedname - NVISIBLE + 1;
	      }
	    break;
	  case UIKEY_HOME:
	    if (selectedname)
	      {
		uih->display = 1;
		selectedname = namestart = 0;
	      }
	    break;
	  case UIKEY_END:
	    if (selectedname < nnames - 1)
	      {
		uih->display = 1;
		selectedname = nnames - 1;;
		if (selectedname >= namestart + NVISIBLE)
		  namestart = selectedname - NVISIBLE + 1;
	      }
	    break;
	  case '\n':
	  case 13:
	    setname (selectedname);
	    uih->display = 1;
	    active = AFILE;
	    break;
	  }
	break;
      case ADIRLIST:
	switch (k)
	  {
	  case '\t':
	  case UIKEY_RIGHT:
	    uih->display = 1;
	    active = AFILE;
	    break;
	  case UIKEY_LEFT:
	    uih->display = 1;
	    active = AFILELIST;
	    break;
	  case UIKEY_UP:
	    if (selecteddir)
	      {
		uih->display = 1;
		selecteddir--;
		if (selecteddir < dirstart)
		  dirstart = selecteddir;
	      }
	    break;
	  case UIKEY_DOWN:
	    if (selecteddir < ndirs - 1)
	      {
		uih->display = 1;
		selecteddir++;
		if (selecteddir >= dirstart + NVISIBLE)
		  dirstart = selecteddir - NVISIBLE + 1;
	      }
	    break;
	  case UIKEY_HOME:
	    if (selecteddir)
	      {
		uih->display = 1;
		selecteddir = dirstart = 0;
	      }
	    break;
	  case UIKEY_END:
	    if (selecteddir < ndirs - 1)
	      {
		uih->display = 1;
		selecteddir = ndirs - 1;;
		if (selecteddir >= dirstart + NVISIBLE)
		  dirstart = selecteddir - NVISIBLE + 1;
	      }
	    break;
	  case '\n':
	  case 13:
	    setdir (selecteddir);
	    uih->display = 1;
	    break;
	  }
	break;
      case AFILE:
	if (ui_textkey (filename, k))
	  break;
	if (k == '\t' || k == UIKEY_DOWN)
	  {
	    active++;
	    uih->display = 1;
	    activebutton = 0;
	  }
	if (k == UIKEY_UP)
	  {
	    active--;
	    uih->display = 1;
	  }

	if (k == '\n' || k == 13)
	  {
	    ui_closefilesel (1);
	  }
	break;
      case AOK:
	if (k == '\n' || k == 13)
	  ui_closefilesel (!activebutton);
	if (k == '\t' || k == UIKEY_RIGHT || k == UIKEY_DOWN)
	  {
	    uih->display = 1;
	    activebutton++;
	    if (activebutton > 2)
	      {
		activebutton = 0;
		active = ADIR;
	      }
	  }
	if (k == UIKEY_LEFT || k == UIKEY_UP)
	  {
	    uih->display = 1;
	    activebutton--;
	    if (activebutton < 0)
	      {
		activebutton = 0;
		active = AFILE;
	      }
	  }
      }
  return 1;
}

int
ui_mousefilesel (int x, int y, int buttons, int flags)
{
  static int grabbed = -1;
  if (!filevisible)
    return 0;
  if (grabbed >= 0 && (flags & MOUSE_DRAG))
    {
      if (!grabbed)
	{
	  int pos;
	  pos =
	    (y - LISTSTART) * nnames / (LISTEND - LISTSTART -
					2 * BORDERHEIGHT);
	  if (pos >= nnames - NVISIBLE)
	    pos = nnames - NVISIBLE;
	  if (pos < 0)
	    pos = 0;
	  if (pos != namestart)
	    {
	      namestart = pos;
	      uih->display = 1;
	      if (selectedname < pos)
		selectedname = pos;
	      if (selectedname >= pos + NVISIBLE)
		selectedname = pos + NVISIBLE - 1;
	    }
	}
      else
	{
	  int pos;
	  pos =
	    (y - LISTSTART) * ndirs / (LISTEND - LISTSTART -
				       2 * BORDERHEIGHT);
	  if (pos >= ndirs - NVISIBLE)
	    pos = ndirs - NVISIBLE;
	  if (pos < 0)
	    pos = 0;
	  if (pos != dirstart)
	    {
	      dirstart = pos;
	      uih->display = 1;
	      if (selecteddir < pos)
		selecteddir = pos;
	      if (selecteddir >= pos + NVISIBLE)
		selecteddir = pos + NVISIBLE - 1;
	    }
	}
    }
  else
    grabbed = -1;
  if (x < filex || y < filey || x > filex + filewidth
      || y > filex + fileheight)
    {
      if (flags & MOUSE_PRESS)
	ui_closefilesel (0);
      return 1;
    }
  if (y < LISTSTART)
    {
      if (pressedbutton != -1)
	pressedbutton = -1, uih->display = 1;
      if ((flags & MOUSE_MOVE) && active != ADIR)
	active = ADIR, uih->display = 1;
      if (flags & MOUSE_PRESS)
	ui_textmouse (dir, x, y);
    }
  else if (y < LISTEND)
    {
      int mouseat = 0;
      if (pressedbutton != -1)
	pressedbutton = -1, uih->display = 1;
      if (x > filex + filewidth / 2)
	mouseat = 1, x -= filewidth / 2;
      x -= filex;
      if (flags & MOUSE_MOVE)
	{
	  if (!mouseat && active != AFILELIST)
	    active = AFILELIST, uih->display = 1;
	  if (mouseat && active != ADIRLIST)
	    active = ADIRLIST, uih->display = 1;
	}
      if (x > LISTWIDTH && (flags & MOUSE_PRESS))
	grabbed = mouseat;
      else
	{
	  if (flags & MOUSE_PRESS)
	    {
	      int atitem =
		(y - LISTSTART - BORDERHEIGHT) / xtextheight (uih->font);
	      if (atitem < 0)
		atitem = 0;
	      if (!mouseat)
		{
		  atitem += namestart;
		  if (atitem < nnames)
		    {
		      if (atitem == selectedname
			  && !strcmp (names[selectedname], filename->text))
			{
			  ui_closefilesel (1);
			}
		      else
			{
			  selectedname = atitem;
			  uih->display = 1;
			  setname (selectedname);
			}
		    }
		}
	      else
		{
		  atitem += dirstart;
		  if (atitem < ndirs)
		    {
		      selecteddir = atitem;
		      uih->display = 1;
		      setdir (selecteddir);
		    }
		}
	    }
	}
    }
  else if (y < OKSTART)
    {
      if (pressedbutton != -1)
	pressedbutton = -1, uih->display = 1;
      /*exit(1); */
      if ((flags & MOUSE_MOVE) && active != AFILE)
	active = AFILE, uih->display = 1;
      if (flags & MOUSE_PRESS)
	ui_textmouse (filename, x, y);
    }
  else
    {
      int mouseat = 0;
      if (x > filex + filewidth / 2)
	mouseat = 1;
      if (flags & MOUSE_PRESS)
	{
	  if (active != AOK)
	    active = AOK, uih->display = 1;
	  if (activebutton != mouseat || pressedbutton != mouseat)
	    activebutton = pressedbutton = mouseat, uih->display = 1;
	}
      if ((flags & MOUSE_MOVE) && pressedbutton != mouseat)
	uih->display = 1, pressedbutton = -1, active = AOK, activebutton =
	  mouseat;
      if ((flags & MOUSE_RELEASE) && pressedbutton == mouseat)
	ui_closefilesel (!mouseat);
    }
  return 1;
}

void
ui_buildfilesel (CONST char *f, CONST char *m, void (*c) (CONST char *, int))
{
  if (filevisible)
    {
      x_fatalerror ("Internal error!");
    }
  pressedbutton = activebutton = active = -1;
  if (lastdir[0] == 0)
    getcwd (lastdir, 256);
  lastdir[255] = 0;
  currdir = mystrdup (lastdir);
  callback = c;
  active = AFILE;
  filex = 0;
  filey = 0;
  filewidth = uih->image->width;
  fileheight = uih->image->height;
  namestart = dirstart = 0;
  mask = m;
  dir =
    ui_opentext (filex + 2 * BORDERWIDTH, DIRSTART + BORDERHEIGHT,
		 filewidth - 4 * BORDERWIDTH, lastdir);
  filename =
    ui_opentext (filex + 2 * BORDERWIDTH, FILESTART + BORDERHEIGHT,
		 filewidth - 4 * BORDERWIDTH, f);
  filevisible = 1;
  ui_buildnames (LISTWIDTH);
  filew = uih_registerw (uih, filepos, drawfile, 0, DRAWBORDER);
}
#endif
