#include "global.hh"

#include <math.h>

#include "config.hh"
#include "anim.hh"
#include "main.hh"

Anim anim;

//********************************************************************

/*
AnimServer as;

struct AnimServer
{
};

AnimServer::AnimServer ()
{
}

int
AnimServer::add (Anim *)
{
}
*/

//********************************************************************

AnimStyle as;

#define AS_START_SET   (1 << 0)
#define AS_END_SET     (1 << 1)

AnimStyle::AnimStyle ()
{
	lwd = 2;
	color_from = black;
	color_to = white;
	flags = 0;
	steps = 10;
	tick = 1; // in milliseconds
	trail = 5; // number of rectangles simultaneously on the screen
}

int
ConfigParser::parse_anim_style ()
{
	skip (".");

	/**/ if (match ("start"))
	{
		geom (as.start.x, as.start.y, as.start.wd, as.start.ht);
		as.flags |= AS_START_SET;
	}
	else if (match ("end"))
	{
		geom (as.end.x, as.end.y, as.end.wd, as.end.ht);
		as.flags |= AS_END_SET;
	}
	else if (match ("steps"))
	{
		num (as.steps);
		if (as.steps < 0)
		{
			as.steps = 0;
			error ("number of animation steps must be >= 0; set to zero");
		};

		if (as.steps > MAX_ANIM_STEPS)
		{
			as.steps = MAX_ANIM_STEPS;
			error ("too much animation steps; reduced to %d", MAX_ANIM_STEPS);
		};
	}
	else if (match ("trail")) num (as.trail);
	else if (match ("tick")) num (as.tick);
	else if (match ("color.from")) as.color_from.parse ();
	else if (match ("color.to")) as.color_to.parse ();
	else if (match ("linewidth")) num (as.lwd);
	else
		error ("bad AnimStyle command: ");
}

int
AnimStyle::init ()
{
	if (!(flags & AS_START_SET))
		start.set (-5, root.ht - 5, 10, 10);

	if (!(flags & AS_END_SET))
		end.set (root.wd - 5, -5, 10, 10);

	// colors
	int i;
	char cn[30]; // to be sure

	int rd, gd, bd;
	int rf, gf, bf;
	int rt, gt, bt;

	color_from.init ();
	color_to.init ();

	rf = color_from.xcolor.red;
	gf = color_from.xcolor.green;
	bf = color_from.xcolor.blue;

	rt = color_to.xcolor.red;
	gt = color_to.xcolor.green;
	bt = color_to.xcolor.blue;

	rd = rt - rf;
	gd = gt - gf;
	bd = bt - bf;

	MyColor *colors = new MyColor[steps];
	if (steps == 1)
	{
		colors[1].name = color_from.name;
	}
	else for (i=0; i<steps; i++)
	{
		//#ifdef HAVE_X401
		//    sprintf (cn, "rgb:%x/%x/%x",
		//#else
		sprintf (cn, "#%4.4x%4.4x%4.4x",
			 //#endif
			 rf + (int) (rd * (((double) i) / ((double) steps - 1))),
			 gf + (int) (gd * (((double) i) / ((double) steps - 1))),
			 bf + (int) (bd * (((double) i) / ((double) steps - 1))));
		
		colors[i].name = cn;
		colors[i].init ();
	};

	// gc
	gc = new GC[steps];

	for (i=0; i<steps; i++)
	{
		gcv.function = GXxor;
		gcv.foreground = colors[i].pixel;
		gcv.fill_style = FillSolid;
		gcv.subwindow_mode = IncludeInferiors;
		gcv.line_width = lwd;
		gcv.graphics_exposures = FALSE;
		//  gcv.clip_mask = close_up.clip_mask;
		gcm = GCFunction | GCForeground | GCFillStyle | GCLineWidth | GCSubwindowMode; // | GCClipMask;

		gc[i] = XCreateGC (dpy, root_window, gcm, &gcv);
	};

	delete colors;

	XSync (dpy, 0);

	twd = (root.wd > root.ht) ? root.wd / 50 : root.ht / 50;
	tht = twd;

	// twd = tht = 2*lwd; // ??
}

//********************************************************************

int
PRect::start ()
{
	set (&as.start);
}

int
PRect::end ()
{
	set (&as.end);
}

int
PRect::max ()
{
	x = 0;
	y = 0;
#ifdef DEBUG
	wd = root.wd / 2;
	ht = root.ht / 2;
#else
	wd = root.wd;
	ht = root.ht;
#endif
}

int
PRect::ppos ()
{
	::ppos.save ();
	x = ::ppos.rx;
	y = ::ppos.ry;
	wd = as.lwd;
	ht = as.lwd;
}

int
PRect::imp ()
{
	//  wd = as.twd;
	//  ht = as.tht;
#define TRAVEL_FACTOR 10
	int wwd, hht;

	wwd = wd / TRAVEL_FACTOR;
	hht = ht / TRAVEL_FACTOR;
	
	x += (wd - wwd)/2;
	y += (ht - hht)/2;

	wd = wwd;
	ht = hht;
}

//********************************************************************

ARect::ARect (Rect &ir, GC &igc)
{
	set (&ir);
	gc = igc;
	if (wd < as.lwd) wd = as.lwd;
	if (ht < as.lwd) ht = as.lwd;

	XDrawRectangle (dpy, root_window, gc,
			x + as.lwd/2, y + as.lwd/2,
			wd - as.lwd, ht - as.lwd);
}

ARect::~ARect ()
{
	XDrawRectangle (dpy, root_window, gc,
			x + as.lwd/2, y + as.lwd/2,
			wd - as.lwd, ht - as.lwd);
}

//********************************************************************

Anim::Anim ()
{
	nrects = 0;
	first = NULL;
	type = ANIM_A;
}

int
Anim::delay ()
{
	//  int stat;
	//  wait (&stat);
	// delay
	timer = add_timer (as.tick);
	while (timer->t);
	rm_timer (timer);
}

/*
int
Anim::to2from ()
	// this is one-way -> thermodynamics? AB != BA, mean to2from !=
	// from2to
{
	from.set (&to);
}
*/

int
Anim::add (ARect *n)
{
	if (nrects == as.trail)
		rm ();

	n->next = first;
	first = n;
	nrects++;
}

int
Anim::rm ()
{
	ARect *i, *j;
	for (i=first; i->next; i=i->next);
	if (i==first)
	{
		delete first;
		first = NULL;
	}
	else
	{
		for (j=first; j->next != i; j=j->next);

		delete i;
		j->next = NULL;
	};
	nrects--;
}

int
Anim::run ()
{
	d.x = to.x - from.x;
	d.y = to.y - from.y;
	d.wd = to.wd - from.wd;
	d.ht = to.ht - from.ht;

	XGrabServer (dpy);

	ARect *n;
	Rect rr;

	r.cl ();
	rr.cl ();

	for (ai=0; ai<as.steps; ai++)
	{
		t = ((double) ai/(as.steps-1));

		step ();

		if (rr != r)
		{    
			n = new ARect (r, as.gc[gci]);
			add (n);
			//      XFlush (dpy);
			XSync (dpy, 0);
			delay ();

			rr = r;
		};
	};

	while (first)
	{
		rm ();
		//XFlush (dpy);
		XSync (dpy, 0);
		delay ();
	};

	XUngrabServer (dpy);
	from = to;
}

#define atan1 0.785398163397448
#define fff(t) (atan(t)/atan1)
#define fnc(t) (fff(fff(fff(fff(fff(t))))))
//#define fnc(t) (fff(fff(fff(fff(fff(fff(fff(fff(fff(fff(t)))))))))))
#define lfn(t,d) ((int) (t*d))
#define afn(t,d) ((int) (fnc(t) * (d)))
#define adfnc(t) ((fnc(2.0*t-1.0)+1.0)/2.0)
#define adfn(t,d) ((int) (adfnc(t) * (d)))

/*
// slow in the beginning, fast in the end = "accelerate from"
int
Anim::a_step ()
{
	gci = afn (1-t, as.steps-1);
	r.x = to.x - afn (1-t, d.x);
	r.y = to.y - afn (1-t, d.y);
	r.wd = to.wd - afn (1-t, d.wd);
	r.ht = to.ht - afn (1-t, d.ht);
}

// fast in the beginning, slow in the end = "deccelerate to"
int
Anim::d_step ()
{
	gci = afn (t, as.steps-1);
	r.x = from.x + afn (t, d.x);
	r.y = from.y + afn (t, d.y);
	r.wd = from.wd + afn (t, d.wd);
	r.ht = from.ht + afn (t, d.ht);
}

int
Anim::ad_step ()
{
	gci = as.steps - 1;
	r.x = from.x + adfn (t, d.x);
	r.y = from.y + adfn (t, d.y);
	r.wd = from.wd + adfn (t, d.wd);
	r.ht = from.ht + adfn (t, d.ht);
}
*/

int
Anim::step ()
{
	// should be the pointer to fnc, calling a_step etc., it would be
	// faster
	switch (type)
	{
	case ANIM_L: // linear
		gci = lfn (t, as.steps-1);
		r.x = from.x + lfn (t, d.x);
		r.y = from.y + lfn (t, d.y);
		r.wd = from.wd + lfn (t, d.wd);
		r.ht = from.ht + lfn (t, d.ht);
		break;
	case ANIM_A: // accelerate
		gci = afn (1-t, as.steps-1);
		r.x = to.x - afn (1-t, d.x);
		r.y = to.y - afn (1-t, d.y);
		r.wd = to.wd - afn (1-t, d.wd);
		r.ht = to.ht - afn (1-t, d.ht);
		break;
	case ANIM_D: // deccelerate
		gci = afn (t, as.steps-1);
		r.x = from.x + afn (t, d.x);
		r.y = from.y + afn (t, d.y);
		r.wd = from.wd + afn (t, d.wd);
		r.ht = from.ht + afn (t, d.ht);
		break;
	case ANIM_AD: // acc/decc
		gci = as.steps - 1;
		r.x = from.x + adfn (t, d.x);
		r.y = from.y + adfn (t, d.y);
		r.wd = from.wd + adfn (t, d.wd);
		r.ht = from.ht + adfn (t, d.ht);
		break;
	};
}

int
Anim::imp ()
{
	PRect tto = to;
	to = from;
	to.imp ();
	type = ANIM_D;
	run ();
	to = tto;
}

int
Anim::travel_exp ()
{
	PRect tto = to;
	to.imp ();
	type = ANIM_D;
	run ();
	from = to;
	to = tto;
	type = ANIM_A;
	run ();
}

int
Anim::imp_travel ()
{
	imp ();
	type = ANIM_A;
	run ();
}
