/*****************************************************************************
Author     : Matthew Stroup
Description: Amiga graphics output for v2600 Atari 2600 emulator
Version    : 2.0 Feb 5, 1997
******************************************************************************/

#include "config.h"
#include "version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "vmachine.h"
#include "address.h"
#include "files.h"
#include "colours.h"
#include "keyboard.h"
#include "options.h"
#include "display.h"
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <graphics/display.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <hardware/custom.h>
#include <hardware/cia.h>

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

void __asm chunky2planar(register __a0 UBYTE *chunky, register __a1 PLANEPTR raster);

void __asm c2p8_init (	register __a0 UBYTE *chunky,		// pointer to chunky data
						register __a1 UBYTE *chunky_cmp,	// pointer to chunky comparison buffer
						register __a2 PLANEPTR *planes,		// pointer to planes
						register __d0 ULONG signals1,		// 1 << sigbit1
						register __d1 ULONG signals2,		// 1 << sigbit2
						register __d2 ULONG pixels,			// WIDTH * HEIGHT
						register __d3 ULONG offset,			// byte offset into plane
						register __d4 UBYTE *buff2,			// Chip buffer (size = width*height)
						register __d5 UBYTE *buff3,			// Chip buffer (size = width*height)
						register __a3 struct GfxBase *GfxBase);

void __asm c2p8_go(void);

long get_signals(void);
void free_signals(void);
long get_chunky_mem(void);
void free_chunky_mem(void);
void init_chunky(void);
long get_window(void);

UBYTE *chunky_cmp=0;	// chunky data comparison buffer (preferably in fast ram)
UBYTE *buff2=0;		// blitter buffer (chip ram)
UBYTE *buff3=0;		// blitter buffer (chip ram)

long sigbit1 = -1;		// used by c2p8()
long sigbit2 = -1;		// used by c2p8()

struct Screen *scr=0;
struct Window *win=0;
struct RastPort *rp=0, temprp;
struct ViewPort *vp=0;
struct IntuiMessage *message=0;
struct GfxBase *GfxBase=0;
struct IntuitionBase *IntuitionBase=0;

struct TagItem TagArray[] = {
	// can add other tags here
	TAG_DONE,0
};

char perm[8] = {0, 1, 2, 3, 4, 5, 6, 7};	// bitplane order

PLANEPTR raster=0, temprast=0;		// 8 contiguous bitplanes
struct BitMap bitmap_bm, tempbm;	// The full depth-8 bitmap

struct ExtNewScreen NewScreenStructure = {
	0,0,
	320,200,
	7,
	0,1,
	NULL,
	CUSTOMSCREEN+CUSTOMBITMAP+SCREENQUIET+NS_EXTENDED,
	NULL,
	NULL,
	NULL,
	&bitmap_bm,
	(struct TagItem *)&TagArray
};

struct NewScreen ns = {
	0,0,								// Left edge, Right edge
	320,200,7,						// Width, Height, Depth
	1,2,								// Detail pen, block pen
	SPRITES,							// Display modes
	CUSTOMSCREEN,						// Type
	NULL,							// Font
	NULL,							// Title
	NULL,							// Gadgets
	NULL								// Custom Bitmap
	};

struct NewWindow NewWindowStructure1 = {
	0,0,
	320,200,
	0,1,
	NULL,
	SIMPLE_REFRESH+BORDERLESS+NOCAREREFRESH,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	0,0,
	0,0,
	CUSTOMSCREEN
};

/* Start of image data */
UBYTE *vscreen=0;

/* The width and height of the image, including any magnification */
int vwidth, vheight, theight;

/* The frame rate counter. Used by the -rr switch */
int tv_counter = 0;

/* Optionally set by the X debugger. */
int redraw_flag = 1;

static int bytesize;

long get_screen(void);

UBYTE colors[NUMCOLS];

extern int moudrv_x, moudrv_y, moudrv_but;


#define CIAAPRA 0xBFE001

struct CIA *ciamou = (struct CIA *) CIAAPRA;

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

/* Turns on the television. */
/* returns: 1 for success, 0 for failure */
int tv_on (int argc, char **argv)
{
int i;

	// Make sure necessary ROM libraries are open
	IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",0);
	if (IntuitionBase == NULL) return(0);	

	GfxBase=(struct GfxBase *) OpenLibrary("graphics.library",0);
	if (GfxBase == NULL) return(0);

	if (Verbose) printf ("OK\n  Allocating screen buffer...");

	vscreen = (UBYTE *) AllocMem (64000, MEMF_PUBLIC|MEMF_CLEAR);

	if (!vscreen)
	{
		if (Verbose) printf ("Memory Allocation FAILED\n");
		return (0);
	}

	if (base_opts.video==1)
	{
		if ((win=OpenWindowTags(NULL,
							WA_Title, "Amiga v2600",
							WA_AutoAdjust,TRUE,
							WA_InnerWidth, 320, 
							WA_InnerHeight, 192,
							WA_DragBar, TRUE,
							WA_CloseGadget, TRUE,
							WA_DepthGadget,TRUE,
							WA_GimmeZeroZero, TRUE,
							WA_SmartRefresh, TRUE,
							WA_IDCMP,IDCMP_CLOSEWINDOW,
							TAG_DONE))==NULL) return(0);

		for (i=0; i<NUMCOLS; i++)
		{
			colors[i]=ObtainBestPen(win->WScreen->ViewPort.ColorMap,
						(colortable[i*2] & 0xff0000)<<8,
						(colortable[i*2] & 0x00ff00)<<16,
						(colortable[i*2] & 0x0000ff)<<24,
						OBP_Precision,PRECISION_EXACT,
						TAG_DONE);
		}
		rp=win->RPort;
		InitBitMap(&tempbm, 8, 320, 200);
		if ((temprast=(PLANEPTR)AllocRaster(320, 8*200))==NULL) return (0);
		for(i=0; i<8; i++)
		{
			tempbm.Planes[i] = temprast + (perm[i] * RASSIZE (320, 200));
		}
		InitRastPort(&temprp);
		temprp.BitMap=&tempbm;
	}
	else
	{
		if (get_screen()==0) return(0);
		ShowTitle(scr, 0);
		for (i=0; i<NUMCOLS; i++)
		{
			SetRGB32(&scr->ViewPort,i,
					(colortable[i*2] & 0xff0000)<<8,
					(colortable[i*2] & 0x00ff00)<<16,
					(colortable[i*2] & 0x0000ff)<<24);
			colors[i]=i;
		}
		if (base_opts.video==2)
		{
			if(get_chunky_mem()==0) return(0);
			if(get_signals())
			{
				NewWindowStructure1.Screen = scr;
				if (get_window()==0) return(0);
				// initialize c2p converter
				c2p8_init (	vscreen,
							chunky_cmp,
							&rp->BitMap->Planes[0],
							1 << sigbit1,
							1 << sigbit2,
							64000,
							0,
							buff2,
							buff3,
							GfxBase);
			}
			else return(0);
		}
	}

	bytesize = 64000;

	/* Calculate the video width and height */
	vwidth = tv_width * 2;
	theight = tv_height + 8;
	vheight = theight;

	/* Turn on the keyboard. Must be done after toplevel is created */
	init_keyboard();

	if (Verbose) printf ("OK\n\b");
	return (1);
}

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

/* Turn off the tv. Closes the X connection and frees the shared memory */
void tv_off (void)
{
	if (Verbose) printf ("Switching off...\n");

	if (base_opts.video==2)
	{
		free_chunky_mem();
		free_signals();
	}

	if (base_opts.video!=0)
	{
		if (win) CloseWindow(win);
	}

	if (base_opts.video==1)
	{
		if (temprast) FreeRaster (temprast, 320, 8*200);
	}
	else
	{
		if (scr) CloseScreen(scr);
		if (raster) FreeRaster (raster, 320, 8 * 200);
	}

	if (vscreen) FreeMem(vscreen, 64000);
	if (GfxBase) CloseLibrary((struct Library *)GfxBase);
	if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
}

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

/* Displays the tv screen */
void tv_display (void)
{
	/* Only display if the frame is a valid one. */
	if (tv_counter % base_opts.rr == 0)
	{
		switch(base_opts.video)
		{
			case 0:
				WaitBOVP(&scr->ViewPort);
				chunky2planar(vscreen,raster);
				break;
			case 1:
				WritePixelArray8(rp, 0, 0, 319, 191, vscreen, &temprp);
				if (message=(struct IntuiMessage *)GetMsg(win->UserPort))
				{
					if (message->Class==CLOSEWINDOW) exit(0);
				}
				break;
			case 2:
				c2p8_go();
		}
	}
	tv_counter++;
}

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

/* The Event code. */
void tv_event (void)
{
	read_keyboard();
}

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

UBYTE tv_color (UBYTE b)
{
	return (UBYTE)(b >> 1);
}

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

long get_screen(void)
{
	long depth, ok = 0;

	InitBitMap(&bitmap_bm, 8, 320, 200);	// Full depth-8 bm

	// since c2p_020() needs 8 contiguous bitplanes, it is not
	// possible to just use OpenScreen() or AllocBitmap() as they
	// may give the bitplanes in a few chunks of memory (noncontiguous)

	if( raster = (PLANEPTR)AllocRaster (320, 8 * 200))
	{
		for(depth = 0; depth < 8; depth++)
		{
			bitmap_bm.Planes[depth] = raster + (perm[depth] * RASSIZE (320, 200));
		}
		
		if(scr = OpenScreen( (struct NewScreen *) &NewScreenStructure))
		{
			SetRast(&scr->RastPort, 0);		// clear screen memory
			WaitBlit();					// wait until it's finished
	
			ok = 1;
		}
	}

	return(ok);

}

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

void free_chunky_mem(void)
{
	if(buff3)
		FreeVec(buff3);

	if(buff2)
		FreeVec(buff2);

	if(chunky_cmp)
		FreeVec(chunky_cmp);
}

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

void free_signals(void)
{
	if(sigbit1 != -1)
	{
		Wait (1 << sigbit1);	// wait for last c2p8 to finish pass 3
		FreeSignal(sigbit1);
		sigbit1 = -1;
	}

	if(sigbit2 != -1)
	{
		Wait (1 << sigbit2);	// wait for last c2p8 to finish totally
		FreeSignal(sigbit2);
		sigbit2 = -1;
	}
}

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

long get_chunky_mem(void)
{
	if((chunky_cmp = AllocVec(64000, MEMF_CLEAR+MEMF_ANY))==0) return (0);
	if((buff2 = AllocVec(64000, MEMF_CLEAR+MEMF_CHIP))==0) return (0);
	if((buff3 = AllocVec(64000, MEMF_CLEAR+MEMF_CHIP))==0) return (0);
	return(1);
}

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

long get_signals(void)
{
	long ok = 0;

	if(-1 != (sigbit1 = AllocSignal(-1)))
	{
		SetSignal (1 << sigbit1, 1 << sigbit1); // Initial state is "finished"

		if(-1 != (sigbit2 = AllocSignal(-1)))
		{
			SetSignal (1 << sigbit2, 1 << sigbit2); // Initial state is "finished"

			ok = 1;
		}
	}

	return(ok);

}

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

long get_window(void)
{
	long ok = 0;

	if (win = OpenWindow(&NewWindowStructure1))
	{
		rp = win->RPort;
		ok = 1;
	}

	return(ok);

}

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

void moudrv_read(void)
{
	moudrv_x=win->MouseX;
	moudrv_y=win->MouseY;
	moudrv_but=!(ciamou->ciapra & 0x0040);
}

void moudrv_init(void)
{
}

void moudrv_update(void)
{
}

void moudrv_close(void)
{
}
