/* ------------------------------------------------------------------------- */
/* ----- Let 'em Fly!   V 1.2 ----------- (c) 1991-93 by Oliver Scheel ----- */
/* ------------------------------------------------------------------------- */
/* ----- Module: ltmf_fly.c   Flying Dials --------------------------------- */
/* ------------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <macros.h>
#include <smallaes.h>
#include <vdi.h>
#include <tos.h>
#include <portab.h>

#include <mxmalloc.h>
#include "ltmf_str.h"
#include "ltmf_def.h"

/* ------------------------------------------------------------------------- */

extern	int	vwk_handle,
		planes;

extern	GRECT	wi_max;
extern	int	gl_wchar,
		gl_hchar;

extern	DIALSAV	dsave[];
extern	int	dialno;

extern	int	initflag;
extern	int	magic;

/* ------------------------------------------------------------------------- */

int nfm_dial(int flag, int xs, int ys, int ws, int hs, int x, int y, int w, int h);

void go_super(void);
void go_user(void);
int vwk_init(void);
void vwk_exit(int handle);
void cdecl obj_clsize(OBJECT *tree, int obj, int *x, int *y, int *w, int *h);
void rc_copy(void *src, void *dest);
void rc_vtog(VRECT *src, GRECT *dest);
void rc_gtov(GRECT *src, VRECT *dest);
int rc_intersect(VRECT *v1, VRECT *v2);
int ltmf_init(void);
void _mouse_center(OBJECT *tree, GRECT *gr, int *x, int *y);

/* ------------------------------------------------------------------------- */

int	istrans;	/* is transparent (send no redraw) */

char	mysound[20] = {	0x08, 0x10, 0x09, 0x00, 0x0a, 0x00,
			0x0b, 0x00, 0x0c, 0x20, 0x0d, 0x09,
			0x07, 0xf8, 0x00, 0xe8, 0x01, 0x01,
			0x82, 0x00 };

/* ------------------------------------------------------------------------- */

#ifndef LIGHT

void _dfly(OBJECT *tree, int id, int x1, int y1, int *x2, int *y2, int lf, long msize)
{
	register int	mdx, mdy;
	int	dx, dy,
		dxsav, dysav,
		isct, i, step;
	long	mem_size[2],
		mem_x, mem_y,
		mblock_x, mblock_y;
	DIALSAV	*dsaveid;
	MFDB	ds[2];
	GRECT	gr;
	VRECT	vr[4][2],	/* the four rectangles	*/
		dp[2],		/* dialog coordinates	*/
		mp[2],		/* memory coordinates	*/
		wm;		/* desktop size		*/

	dsaveid = &dsave[id];
	rc_gtov(&wi_max, &wm);
	rc_vtog(&dsaveid->dpos, &gr);
	rc_copy(&dsaveid->dpos, &dp[0]);
	rc_copy(&dsaveid->dpos, &dp[1]);
	dx = *x2 - x1;
	dy = *y2 - y1;
	if((dp[0].v_x1 + dx) < wm.v_x1)
		dx = wm.v_x1 - dp[0].v_x1;
	else if((dp[0].v_x2 + dx) > wm.v_x2)
		dx = wm.v_x2 - dp[0].v_x2;
	if((dp[0].v_y1 + dy) < wm.v_y1)
		dy = wm.v_y1 - dp[0].v_y1;
	else if((dp[0].v_y2 + dy) > wm.v_y2)
		dy = wm.v_y2 - dp[0].v_y2;
	*x2 = x1 + dx;
	*y2 = y1 + dy;

	dxsav = dx;			/* save coordinates	*/
	dysav = dy;

	if(lf)
	{
		mem_x = (gr.g_h / 8) * planes;
		mem_y = (gr.g_w / 8) * planes;
		if(((mem_x + mem_y) * 3) > msize)
			goto leave;
	}
	while(dxsav || dysav)
	{
		if(lf)
		{
			mblock_x = mem_x * min(abs(dxsav), gr.g_w);
			mblock_y = mem_y * min(abs(dysav), gr.g_h);
			step = (int)((mblock_x + mblock_y) / msize) + 1;
			dx = dxsav / step;
			dy = dysav / step;
/*			dx = sgn(dxsav) * min((int)(mblock_x / mem_x * step), abs(dxsav));
			dy = sgn(dysav) * min((int)(mblock_y / mem_y * step), abs(dysav));
*/		}
		dp[1].v_x1 += dx;
		dp[1].v_y1 += dy;
		dp[1].v_x2 += dx;
		dp[1].v_y2 += dy;
		mdx = min(abs(dx), gr.g_w);	/* max dx/dy	*/
		mdy = min(abs(dy), gr.g_h);

		if(!rc_intersect(&dp[0], &dp[1]))
		{
			isct = FALSE;
			mdy = gr.g_h;
		}
		else
			isct = TRUE;

		ds[0].fd_wdwidth = (gr.g_w + 32) / 16;
		ds[0].fd_w = ds[0].fd_wdwidth * 16;
		ds[0].fd_h = mdy;
		if(isct)
		{
			ds[1].fd_wdwidth = (mdx + 32) / 16;
			ds[1].fd_w = ds[1].fd_wdwidth * 16;
			ds[1].fd_h = gr.g_h - mdy;
		}
		else
			ds[1].fd_wdwidth = ds[1].fd_w = ds[1].fd_h = 0;
		for(i = 0; i < 2; i++)
		{
			mem_size[i] = ((long)ds[i].fd_wdwidth * (long)ds[i].fd_h * (long)planes * 2l);
			ds[i].fd_nplanes = planes;
			ds[i].fd_stand = 0;
		}
		ds[0].fd_addr = dsaveid->flybuf;
		ds[1].fd_addr = (void *)((long)ds[0].fd_addr + mem_size[0]);
		graf_mouse(M_OFF, 0l);
		vs_clip(vwk_handle, 0, &wm);
		if(mdy)
		{
			/* Block 1 */
			vr[0][0].v_x1 = dp[1].v_x1;
			vr[0][0].v_y1 = (dy > 0) ? (dp[1].v_y2 - mdy + 1) : dp[1].v_y1;
			vr[0][0].v_x2 = dp[1].v_x2;
			vr[0][0].v_y2 = (dy > 0) ? dp[1].v_y2 : (dp[1].v_y1 + mdy - 1);
			vr[0][1].v_x1 = vr[0][0].v_x1 & 0x0f;
			vr[0][1].v_y1 = 0;
			vr[0][1].v_x2 = vr[0][1].v_x1 + gr.g_w - 1;
			vr[0][1].v_y2 = mdy - 1;

			/* Block 3 */
			vr[2][1].v_x1 = dp[0].v_x1;
			vr[2][1].v_y1 = (dy > 0) ? dp[0].v_y1 : (dp[0].v_y2 - mdy + 1);
			vr[2][1].v_x2 = dp[0].v_x2;
			vr[2][1].v_y2 = (dy > 0) ? (dp[0].v_y1 + mdy - 1) : dp[0].v_y2;
			vr[2][0].v_x1 = dsaveid->mpos.v_x1;
			vr[2][0].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? 0 : (gr.g_h - mdy));
			vr[2][0].v_x2 = dsaveid->mpos.v_x2;
			vr[2][0].v_y2 = vr[2][0].v_y1 + mdy - 1;

			vro_cpyfm(vwk_handle, S_ONLY, vr[0], &dsaveid->dial, &ds[0]);
		}
		if(mdx && isct)
		{
			/* Block 2 */
			vr[1][0].v_x1 = (dx > 0) ? (dp[1].v_x2 - mdx + 1) : dp[1].v_x1;
			vr[1][0].v_y1 = (dy > 0) ? dp[1].v_y1 : dp[0].v_y1;
			vr[1][0].v_x2 = (dx > 0) ? dp[1].v_x2 : (dp[1].v_x1 + mdx - 1);
			vr[1][0].v_y2 = (dy > 0) ? dp[0].v_y2 : dp[1].v_y2;
			vr[1][1].v_x1 = vr[1][0].v_x1 & 0x0f;
			vr[1][1].v_y1 = 0;
			vr[1][1].v_x2 = vr[1][1].v_x1 + mdx - 1;
			vr[1][1].v_y2 = gr.g_h - mdy - 1;

			/* Block 4 */
			vr[3][1].v_x1 = (dx > 0) ? dp[0].v_x1 : (dp[0].v_x2 - mdx + 1);
			vr[3][1].v_y1 = (dy > 0) ? dp[1].v_y1 : dp[0].v_y1;
			vr[3][1].v_x2 = (dx > 0) ? (dp[0].v_x1 + mdx - 1) : dp[0].v_x2;
			vr[3][1].v_y2 = (dy > 0) ? dp[0].v_y2 : dp[1].v_y2;
			vr[3][0].v_x1 = (dx > 0) ? dsaveid->mpos.v_x1 : (dsaveid->mpos.v_x2 - mdx + 1);
			vr[3][0].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? mdy : 0);
			vr[3][0].v_x2 = vr[3][0].v_x1 + mdx - 1;
			vr[3][0].v_y2 = vr[3][0].v_y1 + gr.g_h - mdy - 1;

			vro_cpyfm(vwk_handle, S_ONLY, vr[1], &dsaveid->dial, &ds[1]);
		}
		vro_cpyfm(vwk_handle, S_ONLY, dp, &dsaveid->dial, &dsaveid->dial);
		if(mdx && isct)
			vro_cpyfm(vwk_handle, S_ONLY, vr[3], &dsaveid->mem, &dsaveid->dial);
		if(mdy)
			vro_cpyfm(vwk_handle, S_ONLY, vr[2], &dsaveid->mem, &dsaveid->dial);

		if(isct)
		{
			mp[0].v_x1 = dsaveid->mpos.v_x1 + ((dx > 0) ? mdx : 0);
			mp[0].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? mdy : 0);
			mp[0].v_x2 = dsaveid->mpos.v_x2 - ((dx > 0) ? 0 : mdx);
			mp[0].v_y2 = dsaveid->mpos.v_y1 + gr.g_h - 1 - ((dy > 0) ? 0 : mdy);
			mp[1].v_x1 = (dp[1].v_x1 & 0x0f) + ((dx > 0) ? 0 : mdx);
			mp[1].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? 0 : mdy);
			mp[1].v_x2 = mp[1].v_x1 + gr.g_w - mdx - 1;
			mp[1].v_y2 = dsaveid->mpos.v_y1 + gr.g_h - 1 - ((dy > 0) ? mdy : 0);
			vro_cpyfm(vwk_handle, S_ONLY, mp, &dsaveid->mem, &dsaveid->mem);
		}
		if(mdy)
		{
			rc_copy(&vr[0][1], &vr[0][0]);
			vr[0][1].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? (gr.g_h - mdy) : 0);
			vr[0][1].v_y2 = vr[0][1].v_y1 + mdy - 1;
			vro_cpyfm(vwk_handle, S_ONLY, vr[0], &ds[0], &dsaveid->mem);
		}
		if(mdx && isct)
		{
			rc_copy(&vr[1][1], &vr[1][0]);
			vr[1][1].v_x1 = (dx > 0) ? (mp[1].v_x2 + 1) : (mp[1].v_x1 - mdx);
			vr[1][1].v_y1 = dsaveid->mpos.v_y1 + ((dy > 0) ? 0 : mdy);
			vr[1][1].v_x2 = vr[1][1].v_x1 + mdx - 1;
			vr[1][1].v_y2 = vr[1][1].v_y1 + gr.g_h - mdy - 1;
			vro_cpyfm(vwk_handle, S_ONLY, vr[1], &ds[1], &dsaveid->mem);
		}
		graf_mouse(M_ON, 0l);
		rc_copy(&dp[1], &dsaveid->dpos);
		dsaveid->mpos.v_x1 += dx;
		dsaveid->mpos.v_x1 &= 0x0f;
		dsaveid->mpos.v_x2 = dsaveid->mpos.v_x1 + gr.g_w - 1;
		tree->ob_x += dx;
		tree->ob_y += dy;
		rc_copy(&dp[1], &dp[0]);

		dxsav -= dx;
		dysav -= dy;
	}
leave:	;
}
#endif

void _dmove(OBJECT *tree, int id)
{
	int	mx, my;
	int	savoff;
	int	snap;
	MFDB	ds;
	DIALSAV	*dsaveid;
	GRECT	gr;
	VRECT	vr[2],
		wm;

	snap = (magic && (tree->ob_flags & SNAPIN)) ? TRUE : FALSE;
	dsaveid = &dsave[id];
	rc_gtov(&wi_max, &wm);
	rc_vtog(&dsaveid->dpos, &gr);
	memcpy(&ds, &dsaveid->mem, sizeof(MFDB));
	ds.fd_addr = dsaveid->flybuf;
	graf_mouse(M_OFF, 0l);
	rc_copy(&dsaveid->dpos, &vr[0]);
	rc_copy(&dsaveid->mpos, &vr[1]);
	vs_clip(vwk_handle, 0, &wm);
	vro_cpyfm(vwk_handle, S_ONLY, vr, &dsaveid->dial, &ds);
	graf_mouse(M_ON, 0l);
	istrans = TRUE;
	vwk_exit(vwk_handle);
	nfm_dial(FMD_FINISH, 0, 0, 0, 0, gr.g_x, gr.g_y, gr.g_w, gr.g_h);
/*	vwk_handle = vwk_init();
*/	if(snap)
		savoff = gr.g_x & 0x07;
	graf_dragbox(gr.g_w, gr.g_h, gr.g_x, gr.g_y,
			wi_max.g_x, wi_max.g_y, wi_max.g_w, wi_max.g_h,
			&mx, &my);
	if(snap)
	{
		mx = (mx & 0xfff8) | savoff;
		if((mx + gr.g_w) > (wi_max.g_x + wi_max.g_w))
			mx -= 8;
	}
/*	vwk_exit(vwk_handle);
*/	nfm_dial(FMD_START, 0, 0, 0, 0, mx, my, gr.g_w, gr.g_h);
	vwk_handle = vwk_init();
	graf_mouse(M_OFF, 0l);
	istrans = FALSE;
	rc_copy(&vr[1], &vr[0]);
	rc_copy(&dsaveid->dpos, &vr[1]);
	vro_cpyfm(vwk_handle, S_ONLY, vr, &ds, &dsaveid->dial);
	graf_mouse(M_ON, 0l);
	tree->ob_x += mx - gr.g_x;
	tree->ob_y += my - gr.g_y;
}

/* ------------------------------------------------------------------------- */

long _init_dial(OBJECT *tree, int id)
{
	long	msize;
	GRECT	real;
	VRECT	vreal;
	VRECT	wi_size;
	DIALSAV	*dsav;

	if(!initflag)
		ltmf_init();
	dsav = &dsave[id];
	magic = ((unsigned int)(tree->ob_type & 0xff00) == MAGIC) ? TRUE : FALSE;
	if((id >= 0) && (id < MAXDIAL) && (dsav->mem.fd_addr != NULL)
		&& (letemfly.conf & C_FLY)
		&& !(magic && (tree->ob_flags & NOFLY)))
	{
		msize = (long)MxMalloc(-1) - MEMCUT;
#ifdef LIGHT
		if(((dsav->mem.fd_wdwidth/2 + dsav->mem.fd_h) * planes) > msize)
			goto leave;
#endif
		obj_clsize(tree, ROOT, &real.g_x, &real.g_y, &real.g_w, &real.g_h);
		rc_gtov(&real, &vreal);
		rc_gtov(&wi_max, &wi_size);
		vreal.v_x1 = max(vreal.v_x1, wi_size.v_x1);
		vreal.v_y1 = max(vreal.v_y1, wi_size.v_y1);
		vreal.v_x2 = min(vreal.v_x2, wi_size.v_x2);
		vreal.v_y2 = min(vreal.v_y2, wi_size.v_y2);
		if((vreal.v_x1 >= dsav->dpos.v_x1) && (vreal.v_y1 >= dsav->dpos.v_y1)
			&& (vreal.v_x2 <= dsav->dpos.v_x2) && (vreal.v_y2 <= dsav->dpos.v_y2))
		{
			dsav->mpos.v_x1 += (vreal.v_x1 - dsav->dpos.v_x1);
			dsav->mpos.v_y1 += (vreal.v_y1 - dsav->dpos.v_y1);
			dsav->mpos.v_x2 -= (dsav->dpos.v_x2 - vreal.v_x2);
			dsav->mpos.v_y2 -= (dsav->dpos.v_y2 - vreal.v_y2);
			rc_copy(&vreal, &dsav->dpos);
			return(msize);
		}
	}
leave:	return(0l);
}

/* ------------------------------------------------------------------------- */

#ifdef LIGHT

void cdecl di_fly(OBJECT *tree)
{
	long	msize;
	DIALSAV	*dsav;

	wind_get(0, WF_WORKXYWH, &wi_max.g_x, &wi_max.g_y, &wi_max.g_w, &wi_max.g_h);

	dsav = &dsave[dialno];
	msize = _init_dial(tree, dialno));
	if(dsav->msize < msize)
	{
		dsav->flybuf = MxMalloc(dsav->msize);
		wind_update(BEG_UPDATE);
		vwk_handle = vwk_init();
		graf_mouse(FLAT_HAND, 0l);
		_dmove(tree, dialno);
		graf_mouse(ARROW, 0l);
		Mfree(dsav->flybuf);
		vwk_exit(vwk_handle);
		wind_update(END_UPDATE);
	}
}

#else

void cdecl di_fly(OBJECT *tree)
{
	int	mx, my,
		old_x, old_y,
		mbut, mk, evnt, d,
		lowflame;
	int	savoff,
		snap,
		flswitch;
	long	msize;
	GRECT	gr;
	DIALSAV	*dsav;

	wind_get(0, WF_WORKXYWH, &wi_max.g_x, &wi_max.g_y, &wi_max.g_w, &wi_max.g_h);
	dsav = &dsave[dialno];
	msize = _init_dial(tree, dialno);
	if(msize)
	{
		wind_update(BEG_UPDATE);
		vwk_handle = vwk_init();
		snap = (magic && (tree->ob_flags & SNAPIN)) ? TRUE : FALSE;
		if(dsav->msize < msize)
		{
			dsav->flybuf = MxMalloc(dsav->msize);
			lowflame = FALSE;
		}
		else
		{
			dsav->flybuf = MxMalloc(msize);
			lowflame = TRUE;
		}
		graf_mouse(FLAT_HAND, 0l);
		graf_mkstate(&old_x, &old_y, &mbut, &mk);
		rc_vtog(&dsav->dpos, &gr);
		flswitch = letemfly.conf & C_FLYSWI;
		if((mbut & 0x02) || (mk & 0x0f))
			flswitch = !flswitch;
		if(flswitch && !lowflame)
			_dmove(tree, dialno);
		else if(letemfly.conf & C_FLYTYP)
		{
			if(snap)
				savoff = dsav->dpos.v_x1 & 0x07;
			do
			{
				evnt = evnt_multi(MU_BUTTON|MU_M1, 1, 1, 0,
							1, old_x, old_y, 1, 1, 0, 0, 0, 0, 0, 0l, 0, 0,
							&mx, &my, &mbut, &d, &d, &d);
				if(evnt & MU_M1)
				{
					_dfly(tree, dialno, old_x, old_y, &mx, &my, lowflame, msize);
					old_x = mx;
					old_y = my;
				}
			}
			while(!(evnt & MU_BUTTON));
			if(snap)
			{
				old_x = dsav->dpos.v_x1;
				mx = (dsav->dpos.v_x1 & 0xfff8) | savoff;
				if((mx + gr.g_w) > (wi_max.g_x + wi_max.g_w))
					mx -= 8;
				_dfly(tree, dialno, old_x, old_y, &mx, &my, lowflame, msize);
			}
		}
		else
		{
			if(snap)
				savoff = gr.g_x & 0x07;
			vwk_exit(vwk_handle);
			graf_dragbox(gr.g_w, gr.g_h, gr.g_x, gr.g_y,
					wi_max.g_x, wi_max.g_y, wi_max.g_w, wi_max.g_h,
					&mx, &my);
			vwk_handle = vwk_init();
			if(snap)
			{
				mx = (mx & 0xfff8) | savoff;
				if((mx + gr.g_w) > (wi_max.g_x + wi_max.g_w))
					mx -= 8;
			}
			_dfly(tree, dialno, gr.g_x, gr.g_y, &mx, &my, lowflame, msize);
		}
		graf_mouse(ARROW, 0l);
		Mfree(dsav->flybuf);
		vwk_exit(vwk_handle);
		wind_update(END_UPDATE);
	}
}

/* ------------------------------------------------------------------------- */

int cdecl di_moveto(OBJECT *tree, int mx, int my)
{
	long	msize;
	int	d;
	DIALSAV	*dsav;
	GRECT	gr;
	int	gong;

	if(!initflag)
		ltmf_init();
	wind_get(0, WF_WORKXYWH, &wi_max.g_x, &wi_max.g_y, &wi_max.g_w, &wi_max.g_h);
	dsav = &dsave[dialno];
	msize = _init_dial(tree, dialno);
	if((mx >= 0) && msize && (letemfly.conf & C_JUMP))
	{
		wind_update(BEG_UPDATE);
		go_super();
		gong = (int)(*(char *)0x0484) & 0x04;
		go_user();
		if(gong)
			Dosound(mysound);
		dsav->flybuf = MxMalloc(msize);
		rc_vtog(&dsav->dpos, &gr);
		_mouse_center(tree, &gr, &mx, &my);
		if(dsav->msize < msize)
			graf_movebox(gr.g_w, gr.g_h, gr.g_x, gr.g_y, mx, my);
		vwk_handle = vwk_init();
		_dfly(tree, dialno, gr.g_x, gr.g_y, &mx, &my, (msize < dsav->msize), msize);
		vwk_exit(vwk_handle);
		Mfree(dsav->flybuf);
		wind_update(END_UPDATE);
		evnt_button(1, 1, 0, &d, &d, &d, &d);
	}
	if(mx == -2)
	{
		return(msize && !(letemfly.conf & C_REDRAW));
	}
	else if(dsav->msize < msize)
		return(1);
	else if(msize)
		return(2);
	else
		return(0);
}

int cdecl di_center(OBJECT *tree)
{
	wind_get(0, WF_WORKXYWH, &wi_max.g_x, &wi_max.g_y, &wi_max.g_w, &wi_max.g_h);
	return(di_moveto(tree, wi_max.g_x + wi_max.g_w / 2, wi_max.g_y + wi_max.g_h / 2));
}

#endif

/* ------------------------------------------------------------------------- */
