#include <config.h>
#ifndef _plan9_
#ifndef NO_MALLOC_H
#include <malloc.h>
#endif
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#ifdef __EMX__
#include <float.h>
/*
   #ifndef M_PI
   #define M_PI 3.14159265358979323846
   #endif
 */
#endif
#else
#include <u.h>
#include <libc.h>
#include <ctype.h>
#endif
#include <fconfig.h>
#include <filter.h>
#include <ui_helper.h>
#include <plane.h>
#include "../include/timers.h"
#include <zoom.h>
#include <xmenu.h>
#include <xerror.h>
#include "autopilot.h"
#include "grlib.h"
#include "play.h"
#include <archaccel.h>

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

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define WAITTIME  50000
#define WAITTIME1 200000
#define WAITTIME2 2000000
#define uih_palettechg(uih) if(!uih->recalculatemode&&uih->queue->palettechg!=NULL) uih->recalculatemode=UIH_PALETTEDRAW, uih->display=1
#include <misc-f.h>

static struct filter *uih_getinstance (CONST struct filteraction *a);
static void uih_destroyinstance (struct filter *f);
static int uih_require (struct filter *f, struct requirements *r);
static int uih_initialize (struct filter *f, struct initdata *i);
static CONST rgb_t uicolors[] = {
  {0, 0, 0},
  {255, 255, 255},
  {255, 65, 0},
  {64, 64, 64},
  {128, 128, 128},
  {128 + 64, 128 + 64, 128 + 64}
};
static CONST rgb_t uibwcolors[] = {
  {0, 0, 0},
  {255, 255, 255},
  {255, 255, 255},
  {255, 255, 255},
  {255, 255, 255},
  {255, 255, 255}
};

static CONST struct filteraction uih_filter = {
  "XaoS's user interface layer",
  "ui",
  0,
  uih_getinstance,
  uih_destroyinstance,
  NULL,
  uih_require,
  uih_initialize,
  convertupgeneric,
  convertdowngeneric,
  NULL
};

static uih_context *uih;
static int waitcount, waitcount1, waitcount2;

extern CONST struct filteraction interlace_filter, stereogram_filter,
  subwindow_filter, smalliter_filter, julia_filter, blur_filter, edge_filter,
  edge2_filter, rotate_filter, starfield_filter, truecolor_filter,
  fixedcolor_filter, bitmap_filter, emboss_filter, palette_filter,
  antialias_filter, threed_filter;

CONST struct filteraction *CONST uih_filters[MAXFILTERS] = {
  &edge_filter,
  &edge2_filter,
  &threed_filter,
  &starfield_filter,
  &stereogram_filter,
  &interlace_filter,
  &blur_filter,
  &emboss_filter,
  &palette_filter,
  &antialias_filter,
  &truecolor_filter,
  NULL
};

CONST int uih_nfilters = 11;

static void
uih_invalidatepos (uih_context * uih)
{
  uih->xcenterm = INT_MAX;
  uih->xcenterm = INT_MAX;
}
static void
uih_finishpalette (struct uih_context *uih)
{
  if (uih->image->palette->flags & UNFINISHED)
    {
      if (uih->image->palette->allocfinished != NULL)
	uih->image->palette->allocfinished (uih->image->palette);
      uih->image->palette->flags &= ~UNFINISHED;
    }
}

static void
uih_getcoord (uih_context * uih, int x, int y, number_t * xr, number_t * yr)
{
  uih->uifilter->action->convertdown (uih->uifilter, &x, &y);
  *xr =
    (((number_t)
      (uih->fcontext->rs.nc +
       (x) * ((uih->fcontext->rs.mc - uih->fcontext->rs.nc) /
	      (number_t) uih->zengine->image->width))));
  *yr =
    (((number_t)
      (uih->fcontext->rs.ni +
       (y) * ((uih->fcontext->rs.mi - uih->fcontext->rs.ni) /
	      (number_t) uih->zengine->image->height))));
  rotateback (*(uih->fcontext), *xr, *yr);
}

int
uih_enablefilter (uih_context * c, int n)
{
  if (c->filter[n] == NULL)
    {
      struct filter *f, *f1;
      int i, wascycling = 0;
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      f = uih_filters[n]->getinstance (uih_filters[n]);
      f1 = c->uifilter;
      if (c->fixedcolor != NULL)
	f1 = c->fixedcolor;
      for (i = MAXFILTERS - 1; i > n; i--)
	{
	  if (c->filter[i])
	    f1 = c->filter[i];
	}
      uih_newimage (c);
      insertfilter (f, f1);
      if (!initqueue (c->queue))
	{
	  c->ddatalost = 1;
	  removefilter (f);
	  f->action->destroyinstance (f);
	  if (!initqueue (c->queue))
	    {
	      x_fatalerror
		("Fatal error. Can not continue - initialization of queue can not be performed eigher with or without filter");
	    }
	  if (wascycling)
	    uih_cycling_on (c);
	  if (!strcmp ("palette", uih_filters[n]->shortname))
	    {
	      uih_updatemenus (c, "palettef");
	    }
	  else
	    {
	      uih_updatemenus (c, uih_filters[n]->shortname);
	    }
	  return 0;
	}
      else
	c->filter[n] = f;
      if (wascycling)
	uih_cycling_on (c);
      if (!strcmp ("palette", uih_filters[n]->shortname))
	{
	  uih_updatemenus (c, "palettef");
	}
      else
	{
	  uih_updatemenus (c, uih_filters[n]->shortname);
	}
      return 1;
    }
  return 0;
}

void
uih_disablefilter (uih_context * c, int n)
{
  if (n == c->aliasnum)
    return;
  if (c->filter[n] != NULL)
    {
      int wascycling = 0;
      struct filter *f = c->filter[n];
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      uih_newimage (c);
      removefilter (f);
      if (!initqueue (c->queue))
	{
	  struct filter *f1;
	  int i;
	  c->ddatalost = 1;
	  f1 = c->uifilter;
	  if (c->fixedcolor != NULL)
	    f1 = c->fixedcolor;
	  for (i = MAXFILTERS - 1; i > n; i--)
	    {
	      if (c->filter[i])
		f1 = c->filter[i];
	    }
	  insertfilter (f, f1);
	  if (!initqueue (c->queue))
	    {
	      x_fatalerror ("Fatal error. Can not continue");
	    }
	}
      else
	f->action->destroyinstance (f), c->filter[n] = NULL;
      if (wascycling)
	uih_cycling_on (c);
      if (!strcmp ("palette", uih_filters[n]->shortname))
	{
	  uih_updatemenus (c, "palettef");
	}
      else
	{
	  uih_updatemenus (c, uih_filters[n]->shortname);
	}
    }
}
int
uih_fastrotateenable (uih_context * c)
{
  int wascycling = 0;
  if (c->juliamode)
    uih_disablejulia (c);
  if (!c->fastrotate && !c->juliamode)
    {
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      c->rotatef = rotate_filter.getinstance (&rotate_filter);
      if (c->rotatef == NULL)
	goto end;
      uih_newimage (c);
      addfilter (c->rotatef, c->zengine);
      if (!initqueue (c->queue))
	goto end2;
      if (wascycling)
	uih_cycling_on (c);
      c->fastrotate = 1;
      return 1;
    }
  return 0;
end2:
  removefilter (c->rotatef);
  initqueue (c->queue);
  c->rotatef->action->destroyinstance (c->rotatef);
end:
  if (wascycling)
    uih_cycling_on (c);
  return 0;
}

void
uih_fastrotatedisable (uih_context * c)
{
  if (c->fastrotate)
    {
      int wascycling = 0;
      uih_rotatemode (c, ROTATE_NONE);
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      c->fastrotate = 0;
      removefilter (c->rotatef);
      initqueue (c->queue);
      c->rotatef->action->destroyinstance (c->rotatef);
      uih_newimage (c);
      if (wascycling)
	uih_cycling_on (c);
    }
}
void
uih_rotate (struct uih_context *c, int n)
{
  if (!n)
    uih_fastrotate (c, 0);
  else
    {
      uih_fastrotate (c, 1);
      uih_rotatemode (c, n);
    }
}
static void
uih_fixedcolordisable (uih_context * c)
{
#ifdef SCONVERTORS
  if (c->fixedcolor != NULL)
    {
      int wascycling = 0;
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      initqueue (c->queue);
      removefilter (c->fixedcolor);
      initqueue (c->queue);
      c->fixedcolor->action->destroyinstance (c->fixedcolor);
      c->fixedcolor = NULL;
      uih_newimage (c);
      if (wascycling)
	uih_cycling_on (c);
    }
#endif
}
static int
uih_fixedcolorenable (uih_context * c)
{
#ifdef SCONVERTORS
  CONST struct filteraction *fa = NULL;
  int wascycling = 0;
  preallocpalette (c->palette);
  switch (c->image->palette->type)
    {
#ifdef SFIXEDCOLOR
    case FIXEDCOLOR:
      fa = &fixedcolor_filter;
      break;
#endif
#ifdef SBITMAPS
    case LBITMAP:
    case MBITMAP:
    case LIBITMAP:
    case MIBITMAP:
      fa = &bitmap_filter;
      break;
#endif
    default:
      x_fatalerror ("Unsupported image type. Recompile XaoS");
    }
  if (c->fixedcolor != NULL && c->fixedcolor->action != fa)
    uih_fixedcolordisable (c);
  if (c->fixedcolor == NULL)
    {
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      c->fixedcolor = fa->getinstance (fa);
      if (c->fixedcolor == NULL)
	goto end;
      uih_newimage (c);
      addfilter (c->fixedcolor, c->uifilter->previous);
      if (!initqueue (c->queue))
	goto end2;
      if (wascycling)
	uih_cycling_on (c);
      return 1;
    }
  return 0;
end2:
  removefilter (c->fixedcolor);
  c->fixedcolor->action->destroyinstance (c->fixedcolor);
  c->fixedcolor = NULL;
  initqueue (c->queue);
end:
  if (wascycling)
    uih_cycling_on (c);
  return 0;
#else
  x_fatalerror ("Fixed color not supported, please recompile XaoS");
  return 0;
#endif
}

int
uih_fastrotate (uih_context * c, int mode)
{
  if (mode)
    return (uih_fastrotateenable (c));
  uih_fastrotatedisable (c);
  return 1;
}

void
uih_angle (uih_context * c, number_t angle)
{
  if (angle != c->fcontext->angle)
    {
      if (!c->fastrotate)
	{
	  c->fcontext->version++;
	  uih_newimage (c);
	}
      c->fcontext->angle = angle;
      uih_animate_image (c);
    }
}
void
uih_rotatemode (uih_context * c, int mode)
{
  CONST char *names[] = {
    "norotate",
    "mouserotate",
    "controtate"
  };
  if (c->fastrotate)
    {
      if (c->rotatemode != mode)
	{
	  c->rotatemode = mode;
	  if (mode == ROTATE_CONTINUOUS)
	    tl_reset_timer (c->doittimer);
	  uih_updatemenus (c, names[mode]);
	}
    }
}

int
uih_enablejulia (uih_context * c)
{
  int wascycling = 0;
  if (!c->juliamode && c->fcontext->mandelbrot
      /*&&c->fcontext->currentformula->calculate_julia != NULL */ )
    {
      struct filter *addf = c->zengine;
      uih_newimage (c);
      if (c->fastrotate)
	uih_fastrotatedisable (c);
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      if (c->fcontext->currentformula->calculate_julia == NULL
	  || c->fcontext->slowmode)
	c->julia = zoom_filter.getinstance (&zoom_filter);
      else
	c->julia = julia_filter.getinstance (&julia_filter);
      if (c->julia == NULL)
	goto end;

      c->subwindow = subwindow_filter.getinstance (&subwindow_filter);
      if (c->subwindow == NULL)
	goto end2;
      if (c->fcontext->currentformula->calculate_julia != NULL
	  && !c->fcontext->slowmode)
	{
	  c->smalliter = smalliter_filter.getinstance (&smalliter_filter);
	  if (c->smalliter == NULL)
	    goto end3;
	}
      else
	c->smalliter = NULL;
      addfilter (c->subwindow, addf);
      if (c->fcontext->currentformula->calculate_julia != NULL
	  && !c->fcontext->slowmode)
	{
	  addfilter (c->smalliter, addf);
	}
      addfilter (c->julia, addf);
      subwindow_setsecond (c->subwindow, addf);
      if (!initqueue (c->queue))
	goto end4;
      if (c->fcontext->currentformula->calculate_julia == NULL
	  || c->fcontext->slowmode)
	c->juliamode = 2;
      else
	c->juliamode = 1;
      uih_updatemenus (c, "fastjulia");
      return 1;
    }
  uih_updatemenus (c, "fastjulia");
  return 0;
end4:;
  removefilter (c->subwindow);
  removefilter (c->julia);
  if (c->smalliter != NULL)
    removefilter (c->smalliter);
  initqueue (c->queue);
end3:;
  c->smalliter->action->destroyinstance (c->smalliter);
end2:;
  c->subwindow->action->destroyinstance (c->subwindow);
end:;
  c->julia->action->destroyinstance (c->julia);
  if (wascycling)
    uih_cycling_on (c);
  uih_updatemenus (c, "fastjulia");
  return 0;
}

void
uih_disablejulia (uih_context * c)
{
  int wascycling = 0;
  if (c->juliamode)
    {
      uih_newimage (c);
      c->fcontext->version++;
      if (c->cycling)
	uih_cycling_off (c), wascycling = 1;
      removefilter (c->subwindow);
      removefilter (c->julia);
      if (c->smalliter != NULL)
	removefilter (c->smalliter);
      initqueue (c->queue);
      if (c->smalliter != NULL)
	c->smalliter->action->destroyinstance (c->smalliter);
      c->subwindow->action->destroyinstance (c->subwindow);
      c->julia->action->destroyinstance (c->julia);
      if (wascycling)
	uih_cycling_on (c);
      c->juliamode = 0;
      uih_updatemenus (c, "fastjulia");
    }
}
int
uih_setjuliamode (uih_context * c, int mode)
{
  if (mode)
    return uih_enablejulia (c);
  uih_disablejulia (c);
  return 1;
}

void
uih_rotationspeed (uih_context * c, number_t speed)
{
  c->rotationspeed = speed;
}
static void
uih_cyclinghandler (void *userdata, int n)
{
  struct uih_context *uih = (struct uih_context *) userdata;
  int direct;
  if (uih->zengine->fractalc->palette != NULL &&
      uih->zengine->fractalc->palette->cyclecolors == NULL)
    return;
  direct = uih->direction * uih->cyclingdirection * n;
  if (direct > 0)
    direct %= uih->zengine->fractalc->palette->size - 1;
  else
    direct = -((-direct) % (uih->zengine->fractalc->palette->size - 1));
  if (direct)
    {
      uih->paletteshift += direct;
      while (uih->paletteshift < 0)
	uih->paletteshift += uih->zengine->fractalc->palette->size - 1;
      uih->paletteshift =
	uih->paletteshift % (uih->zengine->fractalc->palette->size - 1);
      uih->zengine->fractalc->palette->cyclecolors (uih->zengine->
						    fractalc->palette,
						    direct);
      if (uih->flags & UPDATE_AFTER_PALETTE
	  && (!uih->play || !uih->nonfractalscreen))
	uih->display = 1;
      uih_palettechg (uih);
    }
}
void
uih_cycling_off (struct uih_context *c)
{
  if (c->cycling)
    {
      tl_free_timer (c->cyclingtimer);
      c->cycling = 0;
      uih_updatemenus (c, "cycling");
      uih_updatemenus (c, "rcycling");
    }
}
void
uih_display (struct uih_context *c)
{
  c->display = 1;
  c->nonfractalscreen = 0;
  if (c->clearscreen)
    c->clearscreen = 0;
  c->displaytext = 0;
  c->nletters = 0;
  c->display = 1;
  if (c->text[0] != NULL)
    free (c->text[0]), c->text[0] = NULL;
  if (c->text[1] != NULL)
    free (c->text[1]), c->text[1] = NULL;
  if (c->text[2] != NULL)
    free (c->text[2]), c->text[2] = NULL;
  if (c->play)
    uih_clear_lines (c);
}

void
uih_cycling_stop (struct uih_context *c)
{
  if (c->cycling && !c->stopped)
    {
      tl_remove_timer (c->cyclingtimer);
      c->stopped = 1;
    }
}
void
uih_cycling_continue (struct uih_context *c)
{
  if (c->cycling && c->stopped)
    {
      c->stopped = 0;
      tl_add_timer (syncgroup, c->cyclingtimer);
    }
}
void
uih_loadfile (struct uih_context *c, xio_constpath d)
{
  xio_file f;
  f = xio_ropen (d);
  if (f == NULL)
    {
#ifdef _plan9_
      uih_error (c, (char *) errstr);
#else
      uih_error (c, strerror (errno));
#endif
      return;
    }
  uih_load (c, f, d);
  return;
}

void
uih_loadstr (struct uih_context *c, CONST char *data)
{
  xio_file f;
  f = xio_strropen (data);
  uih_load (c, f, "");
  return;
}

void
uih_playstr (struct uih_context *c, CONST char *data)
{
  xio_file f;
  f = xio_strropen (mystrdup (data));
  uih_replayenable (c, f, "", 1);
  return;
}

void
uih_recalculate (struct uih_context *c)
{
  c->fcontext->version++;
  uih_newimage (c);
}

void
uih_playfile (struct uih_context *c, xio_constpath d)
{
  xio_file f;
  if (c->play)
    uih_replaydisable (c);
  f = xio_ropen (d);
  if (f == NULL)
    {
#ifdef _plan9_
      uih_error (c, (char *) errstr);
#else
      uih_error (c, strerror (errno));
#endif
      return;
    }
  uih_replayenable (c, f, d, 1);
  return;
}

void
uih_playtutorial (struct uih_context *c, CONST char *name)
{
  xio_pathdata tmp;
  xio_file f = XIO_FAILED;

  f = xio_gettutorial (name, tmp);
  if (f == NULL)
    {
      uih_error (c, gettext ("Tutorial files not found. Reinstall XaoS"));
      return;
    }
  uih_replayenable (c, f, tmp, 1);
  if (c->passfunc != NULL)
    {
      c->passfunc (c, 1, gettext ("Preparing first image"), 0);
    }
}
void
uih_loadexample (struct uih_context *c)
{
  xio_pathdata name;
  xio_file f = xio_getrandomexample (name);
  c->errstring = NULL;
  if (f == NULL)
    {
      uih_error (c, gettext ("Could not open examples"));
      return;
    }
  uih_load (c, f, name);
  if (c->errstring == NULL)
    {
      char s[256];
      sprintf (s, gettext ("File %s loaded."), name);
      uih_message (c, s);
    }
}
void
uih_savepngfile (struct uih_context *c, xio_constpath d)
{
  CONST char *s;
  if (c->passfunc != NULL)
    {
      c->passfunc (c, 1, gettext ("Saving image..."), 0);
    }
  if (c->recalculatemode)
    {
      uih_newimage (c);
      uih_clearwindows (c);
      uih_do_fractal (c);
    }
  if (c->interrupt)
    {
      uih_message (c, gettext ("Save interrupted"));
      return;
    }
  c->errstring = NULL;
  s = uih_save (c, d);
  if (s != NULL)
    uih_error (c, s);
  if (c->errstring == NULL)
    {
      char s[256];
      sprintf (s, gettext ("File %s saved."), d);
      uih_message (c, s);
    }
}
void
uih_saveposfile (struct uih_context *c, xio_constpath d)
{
  xio_file f;
  c->errstring = NULL;
  f = xio_wopen (d);
  if (f == XIO_FAILED)
    {
      uih_error (c, gettext ("Can not open file"));
      return;
    }
  uih_save_possition (c, f, UIH_SAVEPOS);
  if (c->errstring == NULL)
    {
      char s[256];
      sprintf (s, gettext ("File %s saved."), d);
      uih_message (c, s);
    }
}
char *
uih_savepostostr (struct uih_context *c)
{
  xio_file f;
  c->errstring = NULL;
  f = xio_strwopen ();
  uih_save_possition (c, f, UIH_SAVEPOS);
  return (xio_getstring (f));
}

void
uih_saveundo (struct uih_context *c)
{
  xio_file f;
  if (c->play)
    return;
  c->errstring = NULL;
  if (c->undo.undos[c->undo.last])
    free (c->undo.undos[c->undo.last]);
  f = xio_strwopen ();
  uih_save_possition (c, f, UIH_SAVEPOS);
  c->undo.undos[c->undo.last] = xio_getstring (f);
  c->undo.last = (c->undo.last + 1) % UNDOLEVEL;
}

void
uih_undo (struct uih_context *c)
{
  xio_file f;
  int pos = c->undo.last - 2;
  if (pos < 0)
    pos = UNDOLEVEL + pos;
  if (c->undo.undos[pos])
    {
      f = xio_strropen (c->undo.undos[pos]);
      c->undo.undos[pos] = NULL;
      c->undo.last = pos;
      uih_load (c, f, "");
    }
}
void
uih_redo (struct uih_context *c)
{
  xio_file f;
  int pos = c->undo.last;
  if (c->undo.undos[pos])
    {
      f = xio_strropen (c->undo.undos[pos]);
      c->undo.undos[pos] = NULL;
      c->undo.last = pos;
      uih_load (c, f, "");
    }
}
extern xio_pathdata configfile;
void
uih_savecfg (struct uih_context *c)
{
  xio_file f;
  c->errstring = NULL;
  f = xio_wopen (configfile);
  if (f == XIO_FAILED)
    {
      uih_message (c, (char *) xio_errorstring ());
      return;
    }
  uih_save_possition (c, f, UIH_SAVEALL);
  if (c->errstring == NULL)
    {
      char s[256];
      sprintf (s, gettext ("File %s saved."), configfile);
      uih_message (c, s);
    }
}
void
uih_saveanimfile (struct uih_context *c, xio_constpath d)
{
  xio_file f;
  c->errstring = NULL;
  if (c->save)
    {
      uih_save_disable (c);
      uih_updatemenus (c, "record");
      return;
    }
  f = xio_wopen (d);
  if (f == XIO_FAILED)
    {
      uih_message (c, (char *) xio_errorstring ());
      return;
    }
  uih_save_enable (c, f, UIH_SAVEANIMATION);
  if (c->errstring == NULL)
    {
      char s[256];
      sprintf (s, gettext ("Recording to file %s enabled."), d);
      uih_message (c, s);
    }
  uih_updatemenus (c, "record");
}







CONST char *
uih_save (struct uih_context *c, xio_constpath filename)
{
  FILE *f;
  CONST char *r;
  uih_cycling_stop (c);
  uih_stoptimers (c);
  uih_clearwindows (c);
  f = fopen (filename, "wb");
  r = writepng (f, c->queue->saveimage);
  uih_cycling_continue (c);
  uih_resumetimers (c);
  return (r);
}

void
uih_setcycling (struct uih_context *c, int speed)
{
  c->cyclingspeed = speed;
  if (c->cyclingspeed < 0)
    c->direction = -1;
  else
    c->direction = 1;
  if (c->cycling)
    {
      if (c->cyclingspeed)
	tl_set_interval (c->cyclingtimer,
			 1000000 / c->cyclingspeed * c->direction);
      else
	tl_set_interval (c->cyclingtimer, 1000000 * 100);
    }
}
int
uih_cycling_on (struct uih_context *c)
{
  if (c->zengine->fractalc->palette != NULL &&
      c->zengine->fractalc->palette->cyclecolors != NULL)
    {
      c->cycling = 1;
      tl_update_time ();
      c->cyclingtimer = tl_create_timer ();
      uih_emulatetimers (c);
      uih_setcycling (c, c->cyclingspeed);
      tl_set_multihandler (c->cyclingtimer, uih_cyclinghandler, c);
      tl_add_timer (syncgroup, c->cyclingtimer);
    }
  else
    {
      uih_updatemenus (c, "cycling");
      uih_updatemenus (c, "rcycling");
      return 0;
    }
  uih_updatemenus (c, "cycling");
  uih_updatemenus (c, "rcycling");
  return 1;
}

int
uih_cycling (struct uih_context *uih, int mode)
{
  if (mode)
    return (uih_cycling_on (uih));
  uih_cycling_off (uih);
  return 1;
}



static void
uih_waitfunc (struct filter *f)
{
  int l;
  tl_process_group (syncgroup, NULL);
  l = tl_lookup_timer (uih->calculatetimer);
  if (uih->interrupt)
    f->interrupt = 1, uih->endtime = l;
  if (uih->interruptiblemode)
    {
      if (f->incalculation && !uih->starttime)
	uih->starttime = l;
      else if (uih->starttime && !f->incalculation && !uih->endtime)
	uih->endtime = l;
      if ((uih->maxtime && l > uih->maxtime && f->readyforinterrupt)
	  || uih->interrupt)
	f->interrupt = 1, uih->endtime = l;
    }
  if ((l) > (waitcount + 1) * WAITTIME)
    {
      int display = 0;
      if (!uih->interruptiblemode && l > (waitcount1 + 1) * WAITTIME1)
	{
	  display = 1;
	  waitcount1 = l / WAITTIME1;
	}
      if (f->image == uih->image && !uih->interruptiblemode
	  && l > (waitcount2 + 1) * WAITTIME2)
	{
	  if (!uih->play)
	    uih->display = 1, uih_finishpalette (uih), display = 1;
	  waitcount2 = l / WAITTIME2;
	}
      if (uih->passfunc != NULL)
	{
	  f->interrupt |=
	    uih->passfunc (uih, display, f->pass,
			   (float) (f->max ? f->pos * 100.0 /
				    f->max : 100.0));
	  uih->display = 0;
	}
      waitcount = l / WAITTIME;
    }
  uih_clearwindows (uih);
}

void
uih_do_fractal (uih_context * c)
{
  int flags;
  int time;

  c->interrupt = 0;
  c->display = 0;
  uih = c;
  if (c->juliamode && !c->fcontext->mandelbrot)
    {
      uih_disablejulia (c);
    }
  if ((c->juliamode == 1
       && (c->fcontext->currentformula->calculate_julia == NULL
	   || c->fcontext->slowmode)) || (c->juliamode == 2
					  && c->fcontext->
					  currentformula->calculate_julia !=
					  NULL && !c->fcontext->slowmode))
    {
      uih_disablejulia (c);
      uih_enablejulia (c);
      c->fcontext->version++;
    }

  tl_update_time ();
  if (c->recalculatemode < c->fastmode && c->emulator == NULL
      && !c->fixedstep)
    c->interruptiblemode = 1;
  else
    c->interruptiblemode = 0;
  if (!c->interruptiblemode && c->recalculatemode > UIH_ANIMATION)
    {
      if (c->longwait != NULL)
	c->longwait (c);
      uih_stoptimers (c);
    }

  tl_update_time ();
  tl_reset_timer (c->calculatetimer);
  c->starttime = 0;
  c->endtime = 0;
  waitcount = tl_lookup_timer (c->calculatetimer) / WAITTIME + 2;
  waitcount1 = tl_lookup_timer (c->calculatetimer) / WAITTIME1 + 1;
  waitcount2 = tl_lookup_timer (c->calculatetimer) / WAITTIME2 + 1;
#ifdef _UNDEFINED_
  waitcount = 2;
  waitcount1 = 1;
  waitcount2 = 1;
#endif
  c->incalculation = 1;

  if (!(c->flags & ROTATE_INSIDE_CALCULATION))
    uih_cycling_stop (c);

  time = tl_lookup_timer (c->doittimer);
  if (c->rotatemode == ROTATE_CONTINUOUS)
    {
      c->fcontext->angle += c->rotationspeed * time / 1000000.0;
    }

  tl_reset_timer (c->doittimer);
  c->indofractal = 1;
  if (c->recalculatemode < UIH_PALETTEDRAW)
    {
      if (c->queue->palettechg != NULL)
	flags =
	  c->queue->palettechg->action->doit (c->queue->palettechg,
					      PALETTEONLY, 0);
      else
	flags = CHANGED;
    }
  else
    flags =
      c->uifilter->previous->action->doit (c->uifilter->previous,
					   c->interruptiblemode ?
					   INTERRUPTIBLE : 0, time);
  c->indofractal = 0;

  if (!(c->flags & ROTATE_INSIDE_CALCULATION))
    uih_cycling_continue (c);

  c->dirty = 0;
  if (c->inanimation)
    c->inanimation--;
  c->ddatalost = 0;
  c->recalculatemode = 0;

  if (flags & ANIMATION)
    c->fastanimation = 1;
  else
    c->fastanimation = 0;
  if (c->emulator)
    c->inanimation = 1;
  if (flags & (ANIMATION | UNCOMPLETTE)
      || (c->rotatemode == ROTATE_CONTINUOUS))
    {
      tl_resume_timer (c->doittimer);
      c->uncomplette = 1;
      c->inanimation = 2;
      if (flags & UNCOMPLETTE)
	c->recalculatemode = UIH_ANIMATION;
      else
	c->recalculatemode = UIH_FILTERANIMATION;
      c->display = 1;
    }
  else
    {
      tl_stop_timer (c->doittimer);
      c->uncomplette = 0;
    }
  if ((flags & CHANGED) && (!c->play || !c->nonfractalscreen))
    {
      c->display = 1;
      if (flags & INEXACT)
	c->dirty = 1;
    }
  else
    c->incalculation = 0;
  uih_callcomplette (c);
  if (c->autopilot)
    c->inanimation = 1;
}

void
uih_prepare_image (uih_context * c)
{
  if (c->play)
    uih_update_lines (c);
  if (c->display)
    {
      uih_clearwindows (c);
      xprepareimage (c->image);
      if (uih_needrecalculate (c))
	uih_do_fractal (c);
    }
}
void
uih_callcomplette (uih_context * c)
{
  if (!c->uncomplette && !c->display && !c->recalculatemode && !c->inanimation
      && c->complettehandler != NULL)
    {
      c->complettehandler (c->handlerdata);
    }
}
void
uih_setcomplettehandler (uih_context * c, void (h) (void *), void *d)
{
  c->complettehandler = h;
  c->handlerdata = d;
}

void
uih_letterspersec (uih_context * c, int n)
{
  if (n < 1)
    n = 1;
  c->letterspersec = n;
}

double
uih_displayed (uih_context * c)
{
  int drawingtime;
  uih_finishpalette (c);
  if (c->indofractal)
    return 0;			/*image is currently calculating */
  if (c->recalculatemode)
    c->display = 1;
  else
    c->display = 0;
  tl_update_time ();
  uih_resumetimers (c);
  c->nonfractalscreen = 0;
  c->nletters = 0;
  if (c->incalculation)
    {
      c->incalculation = 0;
      drawingtime = tl_lookup_timer (c->calculatetimer);
      if (c->emulator)
	drawingtime = 0;
      if (c->lasttime == -1
	  || (drawingtime && c->lasttime
	      && (drawingtime / c->lasttime < 0.2
		  || drawingtime / c->lasttime > 4)))
	c->lasttime = drawingtime;
      c->lasttime = (c->lasttime * 30 + drawingtime) / 31;
      c->lastspeed = c->lasttime ? 1000000.0 / c->lasttime : 100.0;
      if (c->interruptiblemode)
	{
	  int i;
	  int time1, time;
	  time1 = drawingtime;
	  time1 -= c->endtime;
	  time = (drawingtime - c->endtime) + c->starttime;
	  if (c->times[0][0] == -1)
	    {
	      for (i = 0; i < AVRGSIZE; i++)
		c->times[0][i] = time, c->times[1][i] = time1;
	      c->count[0] = time * AVRGSIZE, c->count[1] = time1 * AVRGSIZE;
	    }
	  c->timespos = (c->timespos + 1) % AVRGSIZE;
	  c->count[0] += time - c->times[0][c->timespos];
	  c->count[1] += time1 - c->times[1][c->timespos];
	  c->times[0][c->timespos] = time;
	  c->times[1][c->timespos] = time1;
	  c->maxtime = (c->count[0] * 5) / AVRGSIZE;
	  if (c->step || c->pressed
	      || (c->play
		  && (c->playc->morph || c->playc->morphangle
		      || c->playc->morphjulia || c->playc->lines.morphing))
	      || (c->rotatemode == ROTATE_CONTINUOUS) || c->fastanimation)
	    {
	      if (c->maxtime > 1000000 / 25)
		c->maxtime = c->count[0] * 3 / AVRGSIZE;
	      if (c->maxtime > 1000000 / 15)
		c->maxtime = 1000000 / 15;
	    }
	  else
	    {
	      c->maxtime = 1000000 / 3;
	    }
	  if (c->maxtime < 1000000 / 30)
	    c->maxtime = 1000000 / 30;
	  c->maxtime -= c->count[1] / AVRGSIZE;
	  if (c->maxtime < c->starttime + 10000)
	    c->maxtime = c->starttime + 10000;
	}
    }
  uih_callcomplette (c);
  return (c->lastspeed);
}

void
uih_text (uih_context * c, CONST char *text)
{
  int i, l;
  c->display = 1;
  if (c->text[c->ytextpos])
    free (c->text[c->ytextpos]);
  c->textpos[c->ytextpos] = c->xtextpos;
  c->textcolor[c->ytextpos] = c->color;
  c->displaytext |= 1 << c->ytextpos;
  c->text[c->ytextpos] = mystrdup (text);
  l = (int) strlen (text);
  c->todisplayletters = 0;
  for (i = 0; i < l; i++)
    {
      if (isalnum (text[i]))
	c->todisplayletters++;
      if (text[i] == '-')
	c->todisplayletters += 3;
      if (text[i] == '.')
	c->todisplayletters += 2;
    }
  c->step = 0;
}

void
uih_clearscreen (uih_context * c)
{
  c->clearscreen = 1;
  if (c->save)
    c->savec->clearscreen = 1;
  if (c->displaytext)
    c->displaytext = 0;
  if (c->text[0] != NULL)
    free (c->text[0]), c->text[0] = NULL;
  if (c->text[1] != NULL)
    free (c->text[1]), c->text[1] = NULL;
  if (c->text[2] != NULL)
    free (c->text[2]), c->text[2] = NULL;
  c->nletters = 0;
  c->display = 1;
  if (c->play)
    uih_clear_lines (c);
}

void
uih_settextpos (uih_context * c, int x, int y)
{
  CONST char *names1[] = {
    "ytextposup",
    "ytextposmiddle",
    "ytextposbottom",
  };
  CONST char *names2[] = {
    "xtextposleft",
    "xtextposcenter",
    "xtextposright",
  };
  c->xtextpos = x;
  c->ytextpos = y;
  uih_updatemenus (c, names1[y]);
  uih_updatemenus (c, names2[x]);
}


/*timming routines */

void
uih_tbreak (uih_context * c)
{
  c->tbreak = 1;
}

void
uih_emulatetimers (uih_context * c)
{
  if (c->emulator == NULL)
    return;
  tl_emulate_timer (c->maintimer, c->emulator);
  tl_emulate_timer (c->doittimer, c->emulator);
  if (c->autopilot)
    tl_emulate_timer (c->autopilottimer, c->emulator);
  if (c->cycling)
    {
      tl_emulate_timer (c->cyclingtimer, c->emulator);
    }
  if (c->play)
    {
      tl_emulate_timer (c->playc->timer, c->emulator);
    }
  if (c->save)
    {
      tl_emulate_timer (c->savec->timer, c->emulator);
      tl_emulate_timer (c->savec->synctimer, c->emulator);
    }
}
static void
uih_unemulatetimers (uih_context * c)
{
  tl_unemulate_timer (c->maintimer);
  tl_unemulate_timer (c->doittimer);
  if (c->autopilot)
    tl_unemulate_timer (c->autopilottimer);
  if (c->cycling)
    tl_unemulate_timer (c->cyclingtimer);
  if (c->play)
    {
      tl_unemulate_timer (c->playc->timer);
    }
  if (c->save)
    {
      tl_unemulate_timer (c->savec->timer);
      tl_unemulate_timer (c->savec->synctimer);
    }
}
void
uih_constantframetime (uih_context * c, int time)
{
  if (c->emulator == NULL)
    c->emulator = tl_create_emulator ();
  c->emulatedframetime = time;
  uih_emulatetimers (c);
}

void
uih_noconstantframetime (uih_context * c)
{
  if (c->emulator == NULL)
    return;
  uih_unemulatetimers (c);
  tl_free_emulator (c->emulator);
  c->emulator = NULL;
}

void
uih_stoptimers (uih_context * c)
{
  if (!c->stoppedtimers)
    {
      c->stoppedtimers = 1;
      c->display = 1;
      tl_stop_timer (c->maintimer);
      tl_stop_timer (c->doittimer);
      if (c->autopilot)
	tl_stop_timer (c->autopilottimer);
      if (c->play)
	{
	  tl_stop_timer (c->playc->timer);
	  if (c->cycling)
	    tl_stop_timer (c->cyclingtimer);
	}
      if (c->save)
	{
	  tl_stop_timer (c->savec->timer);
	  tl_stop_timer (c->savec->synctimer);
	  if (c->cycling)
	    tl_stop_timer (c->cyclingtimer);
	}
    }
}
void
uih_slowdowntimers (uih_context * c, int time)
{
  tl_slowdown_timer (c->maintimer, time);
  if (c->autopilot)
    tl_slowdown_timer (c->autopilottimer, time);
  if (c->play)
    {
      tl_slowdown_timer (c->playc->timer, time);
      if (c->cycling)
	tl_slowdown_timer (c->cyclingtimer, time);
    }
  if (c->save)
    {
      tl_slowdown_timer (c->savec->timer, time);
      tl_slowdown_timer (c->savec->synctimer, time);
      if (c->cycling)
	tl_slowdown_timer (c->cyclingtimer, time);
    }
}
void
uih_resumetimers (uih_context * c)
{
  if (c->stoppedtimers)
    {
      c->stoppedtimers = 0;
      tl_resume_timer (c->maintimer);
      if (c->cycling)
	tl_resume_timer (c->cyclingtimer);
      if (c->autopilot)
	tl_resume_timer (c->autopilottimer);
      if (c->play)
	{
	  tl_resume_timer (c->playc->timer);
	}
      if (c->save)
	{
	  tl_resume_timer (c->savec->timer);
	  tl_resume_timer (c->savec->synctimer);
	}
    }
}

/*autopilot implementation */

static void
uih_changed (void)
{
  uih_newimage (uih);
}

static void
uih_autopilothandler (void *uih1, int n)
{
  uih = (uih_context *) uih1;
  do_autopilot (uih, &uih->autopilotx, &uih->autopiloty,
		&uih->autopilotbuttons, uih_changed, n);
}
static INLINE void
uih_zoom (uih_context * uih)
{
  uih->step += uih->speedup * 2 * uih->mul;
  if (uih->step > uih->maxstep)
    uih->step = uih->maxstep;
  else if (uih->step < -uih->maxstep)
    uih->step = -uih->maxstep;
}
static INLINE void
uih_unzoom (uih_context * uih)
{
  uih->step -= uih->speedup * 2 * uih->mul;
  if (uih->step > uih->maxstep)
    uih->step = uih->maxstep;
  else if (uih->step < -uih->maxstep)
    uih->step = -uih->maxstep;
}
static INLINE void
uih_slowdown (uih_context * uih)
{
  if (uih->step > 0)
    {
      if (uih->step < uih->speedup * uih->mul)
	uih->step = 0;
      else
	uih->step -= uih->speedup * uih->mul;
    }
  else if (uih->step < 0)
    {
      if (uih->step > -uih->speedup * uih->mul)
	uih->step = 0;
      else
	uih->step += uih->speedup * uih->mul;
    }
}
static INLINE void
uih_zoomupdate (uih_context * uih)
{
  number_t x;
  number_t y;
  number_t mmul = pow ((double) (1 - uih->step), (double) uih->mul);
  number_t mc = uih->fcontext->s.cr - uih->fcontext->s.rr / 2;
  number_t nc = uih->fcontext->s.cr + uih->fcontext->s.rr / 2;
  number_t mi = uih->fcontext->s.ci - uih->fcontext->s.ri / 2;
  number_t ni = uih->fcontext->s.ci + uih->fcontext->s.ri / 2;
  x = uih->xcenter, y = uih->ycenter;
  mc = x + (mc - x) * (mmul);
  nc = x + (nc - x) * (mmul);
  mi = y + (mi - y) * (mmul);
  ni = y + (ni - y) * (mmul);
  uih->fcontext->s.rr = nc - mc;
  uih->fcontext->s.ri = ni - mi;
  uih->fcontext->s.cr = (nc + mc) / 2;
  uih->fcontext->s.ci = (ni + mi) / 2;
  uih_animate_image (uih);
}


/*main uih loop */

int
uih_update (uih_context * c, int mousex, int mousey, int mousebuttons)
{
  int inmovement = 0;
  int slowdown = 1;
  int time;
  uih = c;

  if (!mousebuttons && uih->lastbuttons)
    uih_saveundo (c);
  uih->lastbuttons = mousebuttons;
  if (c->incalculation)
    return 0;
  if (c->emulator != NULL)
    tl_elpased (c->emulator, c->emulatedframetime);

  if (mousebuttons == (BUTTON1 | BUTTON3))
    mousebuttons = BUTTON2;
  tl_process_group (syncgroup, NULL);	/*First we need to update timming */
  tl_update_time ();
  time = tl_lookup_timer (c->maintimer);
  if (c->autopilot)
    {				/*now handle autopilot */
      tl_process_group (c->autopilotgroup, NULL);
    }
  if (!c->inanimation)
    time = 0;
  if (time > 2000000)
    {
      uih_slowdowntimers (uih, time - 2000000);
      time = 2000000;
    }
  if (c->inanimation)
    c->inanimation--;
  tl_reset_timer (c->maintimer);
  c->mul = (double) time / FRAMETIME;
  if (c->fixedstep)
    c->mul = 0.3;
  if (c->tbreak)
    c->mul = 1, c->tbreak--;
  if (c->mul == 0)
    c->mul = 0.00000001;
  if (c->play)
    {
      uih_playupdate (c);
      if (!c->play)
	{
	  c->inanimation = 2;
	  return 1;
	}
      if (c->playc->lines.morphing)	/*inmovement=1, c->display=1; */
	uih_update_lines (c);
      if (c->step)
	uih_zoomupdate (c), inmovement = 1;
      switch (c->zoomactive)
	{
	case 1:
	  uih_zoom (c), inmovement = 1;
	  break;
	case -1:
	  uih_unzoom (c), inmovement = 1;
	  break;
	default:
	  uih_slowdown (c);
	}
      if (c->playc->morph)
	{
	  int timer = tl_lookup_timer (c->playc->timer) - c->playc->starttime;
	  number_t mmul =	/*(tl_lookup_timer (c->playc->timer) - c->playc->starttime) / (number_t) (c->playc->frametime - c->playc->starttime); */
	    MORPHVALUE (timer, c->playc->frametime - c->playc->starttime,
			c->playc->morphtimes[0], c->playc->morphtimes[1]);
	  number_t srr, drr;
	  number_t mmul1;
	  if (c->playc->source.rr * c->fcontext->windowwidth >
	      c->playc->source.ri * c->fcontext->windowheight)
	    srr = c->playc->source.rr;
	  else
	    srr = c->playc->source.ri;
	  if (c->playc->destination.rr * c->fcontext->windowwidth >
	      c->playc->destination.ri * c->fcontext->windowheight)
	    drr = c->playc->destination.rr;
	  else
	    drr = c->playc->destination.ri;
	  if (srr == drr)
	    mmul1 = mmul;
	  else
	    mmul1 =
	      (exp (log (srr) + ((log (drr) - log (srr)) * mmul)) -
	       srr) / (drr - srr);
	  if (mmul1 > 1)
	    mmul1 = 1;
	  if (mmul1 < 0)
	    mmul1 = 0;
	  inmovement = 1;
	  c->fcontext->s.rr =
	    c->playc->source.rr + (c->playc->destination.rr -
				   c->playc->source.rr) * mmul1;
	  c->fcontext->s.ri =
	    c->playc->source.ri + (c->playc->destination.ri -
				   c->playc->source.ri) * mmul1;
	  c->fcontext->s.cr =
	    c->playc->source.cr + (c->playc->destination.cr -
				   c->playc->source.cr) * mmul1;
	  c->fcontext->s.ci =
	    c->playc->source.ci + (c->playc->destination.ci -
				   c->playc->source.ci) * mmul1;
	  uih_animate_image (c);
	}
      if (c->playc->morphjulia)
	{
	  int timer = tl_lookup_timer (c->playc->timer) - c->playc->starttime;
	  number_t mmul =	/*(tl_lookup_timer (c->playc->timer) - c->playc->starttime) / (number_t) (c->playc->frametime - c->playc->starttime); */
	    MORPHVALUE (timer, c->playc->frametime - c->playc->starttime,
			c->playc->morphjuliatimes[0],
			c->playc->morphjuliatimes[1]);
	  uih_setjuliaseed (uih,
			    c->playc->sr + (c->playc->dr -
					    c->playc->sr) * mmul,
			    c->fcontext->pim =
			    c->playc->si + (c->playc->di -
					    c->playc->si) * mmul);
	  inmovement = 1;
	}
      if (c->playc->morphangle)
	{
	  int timer = tl_lookup_timer (c->playc->timer) - c->playc->starttime;
	  number_t mmul =	/*(tl_lookup_timer (c->playc->timer) - c->playc->starttime) / (number_t) (c->playc->frametime - c->playc->starttime); */
	    MORPHVALUE (timer, c->playc->frametime - c->playc->starttime,
			c->playc->morphangletimes[0],
			c->playc->morphangletimes[1]);
	  uih_angle (uih,
		     c->playc->srcangle + (c->playc->destangle -
					   c->playc->srcangle) * mmul);
	  inmovement = 1;
	}
    }
  else
    {
      if (!c->juliamode)
	{
	  if (c->autopilot)
	    {			/*now handle autopilot */
	      mousex = c->autopilotx;
	      mousey = c->autopiloty;
	      mousebuttons = c->autopilotbuttons;
	      inmovement = 1;
	    }
	  if (c->step)
	    {
	      number_t x;
	      number_t y;
	      if (mousex != c->xcenterm || mousey != c->ycenterm)
		{
		  c->xcenterm = mousex;
		  c->ycenterm = mousey;
		  uih_getcoord (uih, mousex, mousey, &x, &y);
		  c->xcenter = x;
		  c->ycenter = y;
		}
	      uih_zoomupdate (c), inmovement = 1;
	    }
	  c->zoomactive = 0;
	  if (c->rotatemode != ROTATE_MOUSE)
	    switch (mousebuttons)
	      {			/*process buttons */
	      case BUTTON1:
		c->zoomactive = 1;
		inmovement = 1;
		break;
	      case BUTTON3:
		c->zoomactive = -1;
		inmovement = 1;
		break;
	      }
	  uih_saveframe (c);
	  if (c->rotatemode != ROTATE_MOUSE)
	    {
	      c->rotatepressed = 0;
	      switch (mousebuttons)
		{		/*process buttons */
		case BUTTON1:
		  uih_zoom (c), slowdown = 0;
		  break;
		case BUTTON3:
		  uih_unzoom (c), slowdown = 0;
		  break;
		case BUTTON2:
		  {
		    number_t x, y;
		    uih_getcoord (uih, mousex, mousey, &x, &y);
		    if (c->pressed && (c->oldx != x || c->oldy != y))
		      {
			c->fcontext->s.cr -= x - c->oldx;
			c->fcontext->s.ci -= y - c->oldy;
			uih_animate_image (c);
			c->moved = 1;
		      }
		    c->pressed = 1;
		    c->speed = 0;
		    update_view (c->fcontext);
		    uih_getcoord (uih, mousex, mousey, &c->oldx, &c->oldy);
		  }
		  break;
		}
	    }
	  else
	    {
	      if (mousebuttons & BUTTON1)
		{
		  number_t x, y;
		  number_t angle;

		  x = (mousex - c->image->width / 2) * c->image->pixelwidth;
		  y = (mousey - c->image->height / 2) * c->image->pixelheight;
		  angle = -atan2 (x, y) * 180 / M_PI;
		  if (c->rotatepressed)
		    {
		      uih_angle (uih,
				 c->fcontext->angle + angle - c->oldangle);
		    }
		  c->rotatepressed = 1;
		  c->oldangle = angle;
		}
	      else
		c->rotatepressed = 0;
	    }
	  if (!(mousebuttons & BUTTON2))
	    c->pressed = 0;
	  if (slowdown)
	    uih_slowdown (c);
	}
      else
	{
	  if (mousebuttons & BUTTON1)
	    {
	      number_t x, x1 = c->fcontext->pre;
	      number_t y, y1 = c->fcontext->pim;
	      c->zoomactive = 0;
	      uih_getcoord (uih, mousex, mousey, &x, &y);
	      c->fcontext->pre = x;
	      c->fcontext->pim = y;
	      uih_saveframe (c);
	      c->pressed = 1;
	      recalculate (c->fcontext->plane, &c->fcontext->pre,
			   &c->fcontext->pim);
	      if (c->fcontext->pre != x1 || c->fcontext->pim != y1)
		{
		  uih_animate_image (c);
		}
	    }
	  else
	    c->pressed = 0;
	}
    }
  if (!inmovement)
    uih_tbreak (c);
  if (c->uncomplette)
    inmovement = 1;
  if (!c->recalculatemode && !c->display)
    uih_finishpalette (c);
  if (!inmovement)
    uih_callcomplette (c);
  if (c->inanimation < inmovement * 2)
    c->inanimation = inmovement * 2;
  return (inmovement * 2);
}

/*actions that can be used be user interface */

void
uih_autopilot_on (uih_context * c)
{
  if (!c->autopilot)
    {
      clean_autopilot (c);
      uih_autopilothandler (c, 1);
      tl_update_time ();
      uih_resumetimers (c);
      c->autopilottimer = tl_create_timer ();
      c->autopilotgroup = tl_create_group ();
      tl_set_multihandler (c->autopilottimer, uih_autopilothandler, c);
      tl_set_interval (c->autopilottimer, 1000000 / 25);
      tl_reset_timer (c->autopilottimer);
      tl_add_timer (c->autopilotgroup, c->autopilottimer);
      tl_update_time ();
      c->autopilot = 1;
      uih_emulatetimers (c);
      uih_updatemenus (c, "autopilot");
    }
}
void
uih_autopilot_off (uih_context * c)
{
  if (c->autopilot)
    {
      tl_remove_timer (c->autopilottimer);
      tl_free_timer (c->autopilottimer);
      tl_free_group (c->autopilotgroup);
      c->autopilot = 0;
      uih_updatemenus (c, "autopilot");
    }
}
void
uih_mkdefaultpalette (uih_context * c)
{
  if (c->zengine->fractalc->palette == NULL)
    return;
  uih_cycling_stop (c);
  if (mkdefaultpalette (c->zengine->fractalc->palette) != 0)
    {
      uih_newimage (c);
    }
  uih_palettechg (c);
  c->paletteshift = 0;
  c->manualpaletteshift = 0;
  c->palettechanged = 1;
  c->palettetype = 0;
  uih_finishpalette (c);
  uih_cycling_continue (c);
}

void
uih_mkpalette (uih_context * c)
{
  int seed;
  int alg = rand () % PALGORITHMS;
  if (c->zengine->fractalc->palette == NULL)
    return;
  uih_cycling_stop (c);
  if (mkpalette (c->zengine->fractalc->palette, seed = rand (), alg) != 0)
    {
      uih_newimage (c);
    }
  uih_palettechg (c);
  c->paletteshift = 0;
  c->manualpaletteshift = 0;
  c->paletteseed = seed;
  uih_finishpalette (c);
  c->palettechanged = 1;
  c->palettetype = alg + 1;
  uih_cycling_continue (c);
}

/*Basic inicialization routines */

static void
uih_alloctables (uih_context * c)
{
  c->zengine = zoom_filter.getinstance (&zoom_filter);
  if (c->zengine == NULL)
    return;
  c->fcontext =
    make_fractalc (0, c->image->pixelwidth * c->image->width,
		   c->image->pixelheight * c->image->height);
  uih_updatemenus (c, "periodicity") uih_updatemenus (c,
						      "in0")
    uih_updatemenus (c, "int0") uih_updatemenus (c,
						 "out0") uih_updatemenus (c,
									  "outt0")
    uih_updatemenus (c, "plane0") uih_updatemenus (c, "guess3") uih = c;
  c->uifilter = uih_filter.getinstance (&uih_filter);
  c->queue = create_queue (c->uifilter);
  insertfilter (c->zengine, c->uifilter);
}
static int
uih_initqueue (uih_context * c)
{
  return (initqueue (c->queue));
}

void
uih_setmaxstep (uih_context * c, number_t p)
{
  c->maxstep = p;
}

void
uih_setspeedup (uih_context * c, number_t p)
{
  c->speedup = p;
}

void
uih_setmaxiter (uih_context * c, int maxiter)
{
  if (maxiter < 1)
    maxiter = 1;
  if (maxiter > 2000000)
    maxiter = 2000000;
  if (c->fcontext->maxiter != (unsigned int) maxiter)
    {
      c->fcontext->maxiter = maxiter;
      c->fcontext->version++;
      uih_newimage (c);
    }
}

void
uih_setbailout (uih_context * c, number_t bailout)
{
  if (bailout < 0)
    bailout = 0;
  if (c->fcontext->bailout != (number_t) bailout)
    {
      c->fcontext->bailout = bailout;
      c->fcontext->version++;
      uih_newimage (c);
    }
}

void
uih_setincoloringmode (uih_context * c, int mode)
{
  if (mode < 0)
    mode = rand () % INCOLORING;
  if (mode > INCOLORING)
    mode = INCOLORING;
  if (c->fcontext->incoloringmode != mode)
    {
      char str[10];
      c->fcontext->incoloringmode = mode;
      c->fcontext->version++;
      uih_newimage (c);
      sprintf (str, "in%i", mode);
      uih_updatemenus (c, str);
    }
}
void
uih_setintcolor (uih_context * c, int mode)
{
  if (mode < 0)
    mode = rand () % TCOLOR;
  if (mode > TCOLOR)
    mode = TCOLOR;
  if (c->fcontext->intcolor != mode)
    {
      char str[10];
      c->fcontext->intcolor = mode;
      if (c->fcontext->incoloringmode == 10)
	{
	  c->fcontext->version++;
	  uih_newimage (c);
	}
      sprintf (str, "int%i", mode);
      uih_updatemenus (c, str);
    }
}
void
uih_setouttcolor (uih_context * c, int mode)
{
  if (mode < 0)
    mode = rand () % TCOLOR;
  if (mode > TCOLOR)
    mode = TCOLOR;
  if (c->fcontext->outtcolor != mode)
    {
      char str[10];
      c->fcontext->outtcolor = mode;
      if (c->fcontext->coloringmode == 10)
	{
	  c->fcontext->version++;
	  uih_newimage (c);
	}
      sprintf (str, "outt%i", mode);
      uih_updatemenus (c, str);
    }
}
void
uih_setperbutation (uih_context * c, number_t zre, number_t zim)
{
  if (c->fcontext->bre != zre || c->fcontext->bim != zim)
    {
      c->fcontext->bre = zre;
      c->fcontext->bim = zim;
      if (c->fcontext->mandelbrot)
	{
	  c->fcontext->version++;
	  uih_newimage (c);
	}
      uih_updatemenus (c, "uiperturbation");
    }
}
void
uih_perbutation (uih_context * c, int mousex, int mousey)
{
  number_t r, i;
  uih_getcoord (c, mousex, mousey, &r, &i);
  uih_setperbutation (c, r, i);
}

void
uih_setjuliaseed (uih_context * c, number_t zre, number_t zim)
{
  if (c->fcontext->pre != zre || c->fcontext->pim != zim)
    {
      c->fcontext->pre = zre;
      c->fcontext->pim = zim;
      if (c->juliamode)
	{
	  uih_animate_image (c);
	}
      else
	{
	  if (!c->fcontext->mandelbrot)
	    {
	      c->fcontext->version++;
	      if (c->playc && c->playc->morphjulia)
		uih_animate_image (c);
	      else
		uih_newimage (c);
	    }
	}
    }
}
void
uih_setfastmode (uih_context * c, int mode)
{
  CONST char *names[] = {
    "nodynamic",
    "nodynamic",
    "dynamicanimation",
    "dynamicnew",
    "dynamicnew"
  };
  if (mode < 0)
    mode = 0;
  c->fastmode = mode;
  uih_updatemenus (c, names[mode]);
}

void
uih_setoutcoloringmode (uih_context * c, int mode)
{
  if (mode < 0)
    mode = rand () % OUTCOLORING;
  if (mode > OUTCOLORING)
    mode = OUTCOLORING - 1;
  if (c->fcontext->coloringmode != mode)
    {
      char str[10];
      c->fcontext->coloringmode = mode;
      c->fcontext->version++;
      uih_newimage (c);
      sprintf (str, "out%i", mode);
      uih_updatemenus (c, str);
    }
}
void
uih_setplane (uih_context * c, int mode)
{
  int i;

  if (mode < 0)
    mode = 0;
  for (i = 0; planename[i] != NULL; i++);
  if (mode >= i)
    mode = i - 1;
  if (mode < 0)
    mode = rand () % i;
  uih_invalidatepos (c);
  if (c->fcontext->plane != mode)
    {
      char str[10];
      c->fcontext->plane = mode;
      c->fcontext->version++;
      uih_newimage (c);
      sprintf (str, "plane%i", mode);
      uih_updatemenus (c, str);
    }
}
void
uih_screentofractalcoord (uih_context * c, int mousex, int mousey,
			  number_t * re, number_t * im)
{
  uih_getcoord (c, mousex, mousey, re, im);
  recalculate (c->fcontext->plane, re, im);
}

void
uih_setmandelbrot (uih_context * c, int mode, int mousex, int mousey)
{
  if (mode < 0)
    mode = 0;
  if (mode > 1)
    mode = 1;
  if (c->fcontext->mandelbrot != mode)
    {
      c->fcontext->mandelbrot = mode;
      if (c->fcontext->mandelbrot == 0 && !c->juliamode)
	{
	  uih_getcoord (c, mousex, mousey, &c->fcontext->pre,
			&c->fcontext->pim);
	  recalculate (c->fcontext->plane, &c->fcontext->pre,
		       &c->fcontext->pim);
	}
      else
	uih_disablejulia (c);
      c->fcontext->version++;
      uih_newimage (c);
      uih_updatemenus (c, "uimandelbrot");
    }
}
void
uih_setguessing (uih_context * c, int range)
{
  char str[10];
  c->fcontext->range = range;
  if (range <= 1)
    {
      uih_updatemenus (c, "noguess");
    }
  else if (range > 8)
    {
      uih_updatemenus (c, "guessall");
    }
  else
    {
      sprintf (str, "guess%i", range);
      uih_updatemenus (c, str);
    }
}
void
uih_setperiodicity (uih_context * c, int periodicity)
{
  c->fcontext->periodicity = periodicity;
  uih_updatemenus (c, "periodicity");
}

void
uih_interrupt (uih_context * c)
{
  if (c->incalculation)
    c->interrupt = 1;
}

void
uih_stopzooming (uih_context * c)
{
  c->speed = 0;
}

int
uih_updateimage (uih_context * c, struct image *image)
{
  /*exit(); */
  c->image = image;
  c->palette = image->palette;
  c->queue->isinitialized = 0;
  c->ddatalost = 1;
  fractalc_resize_to (c->fcontext, c->image->pixelwidth * c->image->width,
		      c->image->pixelheight * c->image->height);
  c->display = 1;
  c->palette->ncells = sizeof (uicolors) / sizeof (rgb_t);
  c->palette->prergb = uicolors;
  if (c->palette->type & BITMAPS)
    c->palette->prergb = uibwcolors;
  c->inanimation = 2;
  uih_newimage (c);
  if (image->palette->type & (FIXEDCOLOR | BITMAPS))
    uih_fixedcolorenable (c);
  else
    uih_fixedcolordisable (c);
  return (uih_initqueue (c));
}
static void
uih_getcscreensizes (struct uih_context *uih, int *x, int *y, int *w, int *h,
		     void *data)
{
  *x = 0;
  *y = 0;
  if (uih->clearscreen)
    *w = uih->image->width, *h = uih->image->height;
  else
    *w = *h = 0;
}
static void
uih_drawcscreen (struct uih_context *uih, void *data)
{
  if (uih->clearscreen)
    clear_image (uih->image);
}
struct uih_context *
uih_mkcontext (int flags, struct image *image,
	       int (*passfunc) (struct uih_context *, int, CONST char *,
				float),
	       void (*longwait) (struct uih_context *),
	       void (*upd) (struct uih_context *, CONST char *))
{
  uih_context *uih;
  uih = (uih_context *) calloc (sizeof (*uih), 1);	/*setup parameters */
  uih->updatemenus = upd;
  uih->autopilot = 0;
  uih->flags = flags;
  uih->image = image;
  uih->playstring = NULL;
  uih->palette = image->palette;
  uih->menuroot = "root";
  uih->palette->ncells = sizeof (uicolors) / sizeof (rgb_t);
  uih->palette->prergb = uicolors;
  if (uih->palette->type & BITMAPS)
    {
      uih->palette->prergb = uibwcolors;
    }
  uih->speed = 0;
  uih->step = 0;
  uih->color = 0;
  uih->speedup = STEP;
  uih->maxstep = MAXSTEP;
  uih->lasttime = -1;
  uih->recalculatemode = UIH_NEW_IMAGE;
  uih->display = 1;
  uih->fastmode = 2;
  uih_updatemenus (uih, "dynamicanimation");
  uih->aliasnum = -1;
  uih->direction = 1;
  uih->cyclingdirection = 1;
  uih->cyclingspeed = ROTATIONSPEED;
  uih->ddatalost = 1;
  uih->xtextpos = 1;
  uih_updatemenus (uih, "xtextleft");
  uih->complettehandler = 0;
  uih->ytextpos = 1;
  uih_updatemenus (uih, "ytextup");
  uih->display = 0;
  uih->errstring = NULL;
  uih->rotatemode = 0;
  uih_updatemenus (uih, "norotate");
  uih->rotationspeed = 10;
  uih->longwait = longwait;
  uih->passfunc = passfunc;
  uih->nletters = 0;
  uih->letterspersec = 15;
  uih->maintimer = tl_create_timer ();
  uih->calculatetimer = tl_create_timer ();
  uih->doittimer = tl_create_timer ();
  tl_update_time ();
  tl_reset_timer (uih->maintimer);
  tl_reset_timer (uih->calculatetimer);
  tl_stop_timer (uih->doittimer);
  tl_reset_timer (uih->doittimer);
  uih_alloctables (uih);
  uih_initqueue (uih);		/*FIXME return value should not be ignored */
  if (image->palette->type & (FIXEDCOLOR | BITMAPS))
    uih_fixedcolorenable (uih);
  uih_mkdefaultpalette (uih);
  uih_stoptimers (uih);
  clean_autopilot (uih);
  uih_newimage (uih);
  uih->cscreenwindow =
    uih_registerw (uih, uih_getcscreensizes, uih_drawcscreen, 0,
		   NONTRANSPARENTW);
  uih_initmessages (uih);
  uih_inittext (uih);
  uih_emulatetimers (uih);
  uih_setformula (uih, 0);
  uih_saveundo (uih);
  return (uih);
}

void
uih_savepalette (uih_context * c)
{
  if (c->palette2 != NULL)
    destroypalette (c->palette2);
  if (c->zengine->fractalc->palette != NULL)
    c->palette2 = clonepalette (c->zengine->fractalc->palette);
}

void
uih_restorepalette (uih_context * uih)
{
  if (uih->palette2 != NULL)
    {
      if (uih->zengine->fractalc->palette != NULL)
	restorepalette (uih->zengine->fractalc->palette, uih->palette2);
      destroypalette (uih->palette2);
    }
  uih->palette2 = NULL;
  uih_finishpalette (uih);
}

void
uih_setformula (uih_context * c, int num)
{
  set_formula (c->fcontext, num);
  uih_newimage (c);
  uih_updatemenus (c, "uimandelbrot");
  uih_updatemenus (c, "uiperturbation");
  uih_updatemenus (c, c->fcontext->currentformula->shortname);
}

void
uih_initstate (struct uih_context *uih)
{
  int i;
  int ver = uih->fcontext->version;
  uih->step = 0;
  uih->speedup = STEP;
  uih->maxstep = MAXSTEP;
  uih_fastrotatedisable (uih);
  uih_disablejulia (uih);
  uih->color = 0;
  uih_cycling_off (uih);
  for (i = 0; i < uih_nfilters; i++)
    uih_disablefilter (uih, i);
  uih_setperbutation (uih, 0, 0);
  set_formula (uih->fcontext, 0);
  uih_setperiodicity (uih, 1);
  uih_setmaxiter (uih, 170);
  uih_setbailout (uih, 4);
  uih_setincoloringmode (uih, 0);
  uih_setoutcoloringmode (uih, 0);
  uih_setcycling (uih, 30);
  uih_display (uih);
  uih_setfastmode (uih, 2);
  uih_setintcolor (uih, 0);
  uih_setouttcolor (uih, 0);
  uih_setplane (uih, 0);
  uih_setguessing (uih, 3);
  uih_angle (uih, 0);
  uih_rotatemode (uih, 0);
  uih_rotationspeed (uih, 10);
  uih->xtextpos = 1;
  uih->ytextpos = 1;
  if (uih->playc)
    {
      uih->playc->morphtimes[0] = 0;
      uih->playc->morphtimes[1] = 0;
      uih->playc->morphjuliatimes[0] = 0;
      uih->playc->morphjuliatimes[1] = 0;
      uih->playc->morphangletimes[0] = 0;
      uih->playc->morphangletimes[1] = 0;
      uih->playc->morphlinetimes[0] = 0;
      uih->playc->morphlinetimes[1] = 0;
    }
  if (mkdefaultpalette (uih->zengine->fractalc->palette) != 0
      || uih->recalculatemode || uih->fcontext->version != ver)
    {
      uih_newimage (uih);
    }
}


void
uih_freecontext (uih_context * c)
{
  struct filter *f;
  int i;
  if (c->emulator != NULL)
    uih_noconstantframetime (c);
  for (i = 0; i < UNDOLEVEL; i++)
    if (c->undo.undos[i])
      free (c->undo.undos[i]), c->undo.undos[i] = 0;
  while (c->queue->first)
    {
      f = c->queue->first;
      removefilter (c->queue->first);
      f->action->destroyinstance (f);
    }
  uih_destroymessages (c);
  uih_destroytext (c);
  uih_removew (c, c->cscreenwindow);
  free (c->queue);
  free_fractalc (c->fcontext);
  free (c);
}
static struct filter *
uih_getinstance (CONST struct filteraction *a)
{
  struct filter *f = createfilter (a);
  f->data = uih;
  f->name = "XaoS's user interface layer";
  return (f);
}
static void
uih_destroyinstance (struct filter *f)
{
  struct uih_context *c = (struct uih_context *) f->data;
  if (c->autopilot)
    uih_autopilot_off (c);
  if (c->cycling)
    uih_cycling_off (c);
  tl_free_timer (c->maintimer);
  tl_free_timer (c->calculatetimer);
  tl_free_timer (c->doittimer);
  if (c->save)
    uih_save_disable (c);
  if (c->play)
    uih_replaydisable (c);
  free (f);
}
static int wascycling;
static int
uih_require (struct filter *f, struct requirements *r)
{
  struct uih_context *uih;
  uih = (struct uih_context *) f->data;
  f->req = *r;
  uih_clearwindows (uih);
  if (uih->cycling)
    uih_cycling_off (uih), wascycling = 1;
  if (!(r->supportedmask & uih->image->palette->type))
    return 0;
  /*FIXME something should be done here :) */
  return (1);
}

static int
uih_initialize (struct filter *f, struct initdata *i)
{
  struct uih_context *uih;
  int returnval;
  uih = (struct uih_context *) f->data;
  f->queue->saveimage = uih->image;
  i->fractalc = uih->fcontext;
  uih_setfont (uih);
  tl_update_time ();
  f->image = uih->image;
  f->wait_function = uih_waitfunc;
  uih->times[0][0] = -1;
  i->image = uih->image;
  f->fractalc = i->fractalc;
  f->image->palette->flags |= FINISHLATER;
  i->fractalc->palette = uih->image->palette;
  i->wait_function = uih_waitfunc;
  /*FIXME datalost should be handled in better way */
  if (uih->ddatalost)
    i->flags |= DATALOST;
  uih->tbreak = 2;
  uih_invalidatepos (uih);
  clean_autopilot (uih);
  returnval = f->previous->action->initialize (f->previous, i);
  if (wascycling)
    uih_cycling_on (uih), wascycling = 0;
  return returnval;
}
