/*
 * Implementation of the needed X functions for the
 * Commodore Amiga
 *
 * Michael Balzer, December 1992
 * balzer@heike.informatik.uni-dortmund.de
 * m.balzer@aworld.(zer|sub.org|aworld.de)
 *
 * Release 1.0 (8.12.92):
 *		First release, needs os3.0, can only display on
 *		workbench or on 8 bit lores screen.
 *
 * Release 1.1 (11.12.92):
 *		Now also runs on system 2.x. Uses reqtools.library
 *		to ask the user for the screenmode. No HAM support yet.
 *		Version string: 1.22 means Amiga version 2 of mpeg_play 1.2
 *
 * TODO:
 *	- enhance color allocation for 2.x (error += usecount?)
 *	- implement mono style dithering
 *	- implement HAM mode
 */


#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <intuition/classes.h>
#include <graphics/gfxbase.h>
#include <graphics/modeid.h>
#include <graphics/scale.h>
#include <graphics/graphint.h>
#include <dos/dos.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>

#include <libraries/reqtools.h>
#include <proto/reqtools.h>
#define static
#define __inline
#include <inline/minreqtools.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <stdio.h>
#include <stdlib.h>

#define ABS(a) (((int)(a)<0) ? -(a) : (a))


char *_version_ = "\0$VER: mpeg_play 1.22 (" __DATE__ ")";


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


extern
struct Library		*SysBase;

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase		*GfxBase = NULL;
struct ReqToolsBase	*ReqToolsBase = NULL;

struct Screen		*mpeg_screen = NULL;
struct Window		*mpeg_window = NULL;
struct RastPort		*rp = NULL;

struct RastPort		*temprp = NULL;
struct BitMap		*tempbm = NULL;
struct ViewPort		*vp = NULL;

char				*amiga_option = NULL;

int					offx, offy;

struct ColorMap		*cm = NULL;
int					used_colors[ 256 ];
int					used_cnt = 0;

UWORD				saved_colors[ 256 ];
int					saved_cnt = 0;

int					rev3;


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

/* support routines for os revision 2 */

struct BitMap *myAllocBitMap( unsigned long sizex, unsigned long sizey,
	unsigned long depth, unsigned long flags,
	struct BitMap *friend_bitmap )
{
	struct BitMap *bm;
	
	bm = AllocVec( sizeof *bm, MEMF_CLEAR );
	
	if( bm )
	{
		int i;
		
		bm->BytesPerRow = (sizex + 15) >> 3 & 0xfffe;
		bm->Rows = sizey;
		bm->Depth = depth;
		
		for( i=0; i<depth; i++ )
		{
			if( !(bm->Planes[i] = AllocRaster(sizex, sizey)) )
			{
				while( i-- ) FreeRaster( bm->Planes[i], sizex, sizey );
				FreeVec( bm );
				bm = 0;
				break;
			}
		}
	}
	
	return bm;
}


void myFreeBitMap( struct BitMap *bm )
{
	while( bm->Depth-- )
		FreeRaster( bm->Planes[bm->Depth], bm->BytesPerRow*8, bm->Rows );
	FreeVec( bm );
}


LONG myObtainBestPenA( struct ColorMap *cm, unsigned long r, unsigned long g,
	unsigned long b, struct TagItem *tags )
{
	int i, best, err, besterr;
	int br, bg, bb;
	int colors;
	
	/* Farbwerte auf 4 Bit bringen */
	
	r >>= 28; g >>= 28; b >>= 28;
	
	/* Strategie: Zunächst die Farbe suchen, die der gewünschten
	 * am nächsten liegt. Wenn Farbe >= 4, Farbe auf Mittelwert zw.
	 * aktuellen und gesuchten Werten setzen und zurückgeben.
	 * Die unteren vier bleiben unverändert (Workbench).
	 */
	
	best = 0;
	besterr = 100;
	
	colors = 1 << rp->BitMap->Depth;
	
	for( i=0; i<colors; i++ )
	{
		long rgb = GetRGB4( cm, i );
		int cr, cg, cb;
		
		cr = rgb >> 8 & 15;
		cg = rgb >> 4 & 15;
		cb = rgb      & 15;
		
		err = ABS( r - cr )
			+ ABS( g - cg )
			+ ABS( b - cb );
		
		if( err < besterr )
		{
			best = i;
			besterr = err;
			br = cr;
			bg = cg;
			bb = cb;
		}
	}
	
	if( best >= 4 && besterr != 0 )
	{
		/* Farbe anpassen */
		SetRGB4( vp, best,
			(r + br) / 2,
			(g + bg) / 2,
			(b + bb) / 2 );
	}
	
	return best;
}


void myReleasePen( struct ColorMap *cm, unsigned long n )
{
	/* Farbe restaurieren */
	long rgb = saved_colors[n];
	int cr, cg, cb;
	
	cr = rgb >> 8 & 15;
	cg = rgb >> 4 & 15;
	cb = rgb      & 15;
	
	SetRGB4( vp, n, cr, cg, cb );
}


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


void amiga_closedown( void )
{
	if( cm )
	{
		while( used_cnt-- )
			if( rev3 ) ReleasePen( cm, used_colors[used_cnt] );
			else myReleasePen( cm, used_colors[used_cnt] );
	}
	
	if( mpeg_window ) CloseWindow( mpeg_window );
	if( mpeg_screen ) CloseScreen( mpeg_screen );
	
	if( tempbm )
	{
		if( rev3 ) FreeBitMap( tempbm );
		else myFreeBitMap( tempbm );
	}
	if( temprp ) FreeVec( temprp );

	if( ReqToolsBase )	CloseLibrary( (struct Library *) ReqToolsBase );
	if( IntuitionBase )	CloseLibrary( (struct Library *) IntuitionBase );
	if( GfxBase )		CloseLibrary( (struct Library *) GfxBase );
}


Display *XOpenDisplay( _Xconst char *display_name )
{
	struct rtScreenModeRequester *scrmodereq;
	int y;
	
	static struct TagItem windowtags[] =
	{
		{	WA_PubScreen,	NULL },
		{	WA_Left,		32 },
		{	WA_Top,			32 },
		{	WA_Width,		160 },
		{	WA_Height,		120 },
		{	WA_Title,		(ULONG) "MPEG Window" },
		{	WA_IDCMP,		IDCMP_CLOSEWINDOW },
		{	WA_Flags,		WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_NOCAREREFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP },
		{	WA_ScreenTitle,	(ULONG) "MPEG Player V1.2, Amiga version by Michael Balzer 12/92" },
		{	TAG_END }
	};
	
	if( !IntuitionBase )
	{
		/* install closedown routine */
		
		atexit( amiga_closedown );
		
		/* open shared libraries */
		
		IntuitionBase = (struct IntuitionBase *) OpenLibrary( "intuition.library", 0 );
		if( !IntuitionBase ) exit(20);
		
		GfxBase = (struct GfxBase *) OpenLibrary( "graphics.library", 0 );
		if( !GfxBase ) exit(20);
		
		ReqToolsBase = (struct ReqToolsBase *) OpenLibrary( "reqtools.library", 38 );
		if( !ReqToolsBase )
		{
			puts( "I need reqtools.library v38!\n" );
			exit(20);
		}
		
		rev3 = (GfxBase->LibNode.lib_Version >= 39);
		
		/* open some display */
		
		if( strcmp(amiga_option, "window") == 0 )
		{
			/* try to open a window */
			struct Screen *s;
			
			if( s = LockPubScreen(NULL) )
			{
				windowtags[0].ti_Data = (ULONG) s;
				mpeg_window = OpenWindowTagList( NULL, windowtags );
				if( mpeg_window )
				{
					rp = mpeg_window->RPort;
					offx = mpeg_window->BorderLeft;
					offy = mpeg_window->BorderTop;
				}
				
				UnlockPubScreen( NULL, s );
			}
		}
		
		/* fallback to own screen */
		
		if( !mpeg_window )
		{
			if( scrmodereq = rtAllocRequestA( RT_SCREENMODEREQ, NULL ) )
			{
				if( rtScreenModeRequest( scrmodereq, "MPEG: Pick a screen mode",
					RTSC_Flags, SCREQF_DEPTHGAD|SCREQF_SIZEGADS|SCREQF_AUTOSCROLLGAD|SCREQF_OVERSCANGAD,
					TAG_END ) )
				{
					mpeg_screen = OpenScreenTags( NULL,
						SA_Title,		(ULONG) "MPEG Screen",
						SA_DisplayID,	scrmodereq->DisplayID,
						SA_Width,		scrmodereq->DisplayWidth,
						SA_Height,		scrmodereq->DisplayHeight,
						SA_Depth,		scrmodereq->DisplayDepth,
						SA_Overscan,	scrmodereq->OverscanType,
						SA_AutoScroll,	scrmodereq->AutoScroll,
						SA_Quiet,		TRUE,
						SA_Interleaved,	TRUE,
						SA_SharePens,	TRUE,
						TAG_END );
					
					/* Autoscroll would need a backdrop window, but that
					 * would posssibly slow it down even more...
					 */
				}
				
				rtFreeRequest( scrmodereq );

				if( !mpeg_screen )
					exit(20);
				
				rp = &mpeg_screen->RastPort;
				offx = offy = 0;
			}
			else
			{
				puts( "Out of memory!" );
				exit(0);
			}
		}
		
		/* Viewport und Colormap bestimmen */
		
		vp = (mpeg_screen)
			? &mpeg_screen->ViewPort
			: &mpeg_window->WScreen->ViewPort;

		cm = vp -> ColorMap;

		/* Farben sichern */
		
		saved_cnt = 1 << rp->BitMap->Depth;
		for( y=0; y<saved_cnt; y++ )
			saved_colors[y] = GetRGB4( cm, y );
		
		/* temporären Rastport aufsetzen */
		
		temprp = AllocVec( sizeof *temprp, MEMF_ANY );
		memcpy( temprp, rp, sizeof *temprp );
		temprp->Layer = 0;
		
		if( rev3 )
			tempbm = AllocBitMap( rp->BitMap->BytesPerRow * 8, 1,
				rp->BitMap->Depth, NULL, rp->BitMap );
		else
			tempbm = myAllocBitMap( rp->BitMap->BytesPerRow * 8, 1,
				rp->BitMap->Depth, NULL, rp->BitMap );
		
		temprp->BitMap = tempbm;
	}
	
	return (Display *) 1;
}


int XDefaultScreen( Display *display )
{
	return 1;
}


XFlush( Display *display )
{
}


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


Window XRootWindow(
    Display*		display,
    int				screen_number )
{
	return 1;
}


Window XCreateSimpleWindow(
    Display*		display,
    Window			parent,
    int				x,
    int				y,
    unsigned int	width,
    unsigned int	height,
    unsigned int	border_width,
    unsigned long	border,
    unsigned long	background )
{
	return 1;
}


Window XCreateWindow(
    Display*		display,
    Window			parent,
    int				x,
    int				y,
    unsigned int	width,
    unsigned int	height,
    unsigned int	border_width,
    int				depth,
    unsigned int	class,
    Visual*			visual,
    unsigned long	valuemask,
    XSetWindowAttributes*	attributes )
{
	/* Generiert Fenster auf Full-Color-Display */
	/* (HAM Support?) */
	return 0;
}


XSetStandardProperties(
    Display*		display,
    Window			w,
    _Xconst char*	window_name,
    _Xconst char*	icon_name,
    Pixmap			icon_pixmap,
    char**			argv,
    int				argc,
    XSizeHints*		hints )
{
}


XMapWindow(
    Display*		display,
    Window			w )
{
}


XResizeWindow(
    Display*		display,
    Window			w,
    unsigned int	width,
    unsigned int	height )
{
	if( mpeg_screen ) return;
	
	ChangeWindowBox( mpeg_window,
		mpeg_window->LeftEdge, mpeg_window->TopEdge,
		width + mpeg_window->BorderLeft + mpeg_window->BorderRight,
		height + mpeg_window->BorderTop + mpeg_window->BorderBottom
	);
}


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


Status XMatchVisualInfo(
    Display*		display,
    int				screen,
    int				depth,
    int				class,
    XVisualInfo*	vinfo_return )
{
	return( depth <= 8 );
}


XVisualInfo *XGetVisualInfo(
    Display*		display,
    long			vinfo_mask,
    XVisualInfo*	vinfo_template,
    int*			nitems_return )
{
	*nitems_return = 0;
	return vinfo_template;
}


GC XCreateGC(
    Display*		display,
    Drawable		d,
    unsigned long	valuemask,
    XGCValues*		values )
{
	return (GC) 1;
}


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


XImage *XCreateImage(
    Display*		display,
    Visual*			visual,
    unsigned int	depth,
    int				format,
    int				offset,
    char*			data,
    unsigned int	width,
    unsigned int	height,
    int				bitmap_pad,
    int				bytes_per_line )
{
	XImage *xi;
	
	if( xi = calloc(sizeof *xi, 1) )
	{
		xi->width   = width;
		xi->height  = height;
		xi->depth   = depth;
		
		xi->f.destroy_image = (int (*)()) free;
	}
	
	return xi;
}


XPutImage(
    Display*		display,
    Drawable		d,
    GC				gc,
    XImage*			image,
    int				src_x,
    int				src_y,
    int				dest_x,
    int				dest_y,
    unsigned int	width,
    unsigned int	height	   )
{
	struct IntuiMessage	*imsg;
	ULONG				class;

	/* in image->data ist das Bild als Folge von Bytes,
	 * display zeigt auf nix,
	 * d und gc sind egal,
	 * src_? und dest_? sind hier immer 0,
	 * width und height stimmen mit den Bilddimensionen überein
	 * (oder sollten zumindest...)
	 */
	
	WritePixelArray8( rp, offx, offy, offx+width-1, offy+height-9, image->data, temprp );
	/* y-Height - 9 because of 8 lines of random pixels beyond the pic */
	
	if( mpeg_window )
	{
		while( imsg = (struct IntuiMessage *) GetMsg(mpeg_window->UserPort) )
		{
			class = imsg->Class;
			ReplyMsg( (struct Message *) imsg );
			
			if( class == CLOSEWINDOW )
				exit( 0 );
		}
	}
}


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


Colormap XCreateColormap(
    Display*		display,
    Window			w,
    Visual*			visual,
    int				alloc )
{
	return 1;
}


Colormap XDefaultColormap(
    Display*		display,
    int				screen_number )
{
	return 1;
}


Status XAllocColor(
    Display*		display,
    Colormap		colormap,
    XColor*			xcolor )
{
	long color;
	
	if( rev3 )
		color = ObtainBestPenA( cm,
			xcolor->red << 16, xcolor->green << 16, xcolor->blue << 16,
			NULL );
	else
		color = myObtainBestPenA( cm,
			xcolor->red << 16, xcolor->green << 16, xcolor->blue << 16,
			NULL );
	
	used_colors[ used_cnt++ ] = color;
	xcolor->pixel = color;
	
	return color;
}


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


XFree( void *data )
{
}


