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

  osdepend.c

  OS dependant stuff (display handling, keyboard scan...)
  This is the only file which should me modified in order to port the
  emulator to a different system.

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

#include <BeHeaders>
#pragma optimization_level 4

#include "mame.h"
#include "driver.h"
#include "CMAMEDisplay.h"
#include "CAudio.h"
#include "CIOThread.h"
#include "CPalette.h"

#define NUMVOICES 8

/** Joystick additions (moofie++) **/
void center_joystick();

typedef struct joystick_state {
	bool		left;
	bool		right;
	bool		up;
	bool		down;
	bool		button1;
	bool		button2;
	int16	hCenter;
	int16	vCenter;
	int16	hSlop;
	int16	vSlop;
} joystick_state;

CMAMEDisplay			*gDisplay;
CAudio				*gAudio;
bool					gUseMouse;
bool					gUseJoystick;
bool					gUseSound;
BJoystick				*gJoystick;
joystick_state		gJoystickState;

extern bool			gOSDKeys[OSD_MAX_KEY+1];
extern app_info		gAppInfo;

struct osd_bitmap *gBitmap;
status_t			gWinErr;

int	video_sync = false;
int	play_sound = true;
// define these here for now
int joy_left,joy_right,joy_up,joy_down,joy_b1,joy_b2,joy_b3,joy_b4;

int osd_init(int argc,char **argv)
{
	gUseMouse = true;
	gUseJoystick = true;
	gUseSound = true;
	
	for (int i = 1;i < argc;i++) {
		if (!strcasecmp(argv[i], "-nomouse"))
			gUseMouse = false;
		if (!strcasecmp(argv[i], "-nojoy"))
			gUseJoystick = false;
		if (!strcasecmp(argv[i], "-nosound"))
			gUseSound = false;
	}
	
	gDisplay = CMAMEDisplay::CreateMAMEDisplay(argc, argv);
	gAudio = gUseSound ? new CAudio(NUMVOICES) : NULL;

	/** Joystick additions (moofie++) **/
	system_info		systemInfo;
		
	get_system_info(&systemInfo);
	
	//  If we have a BeBox, open the Joystick port and
	//  if that succeeds, center the joystick.  This
	//  will probably also work fine on BeOS for Intel,
	//  but I'm not sure of the exact codes for that.

	if (gUseJoystick && systemInfo.platform_type == B_BEBOX_PLATFORM)
	{
		gJoystick = new BJoystick();
		
		if (gJoystick->Open("joystick1") > 0)
		{
			center_joystick();
		}
		else
		{
			delete gJoystick;
			gJoystick = NULL;
		}
	}
	else
		gJoystick = NULL;
	
	return gDisplay == NULL;
}

void osd_exit(void)
{
	gDisplay->Exit();
	if (gAudio != NULL)
		delete gAudio;
	if (gJoystick != NULL) {
		gJoystick->Close();
		delete gJoystick;
	}
}

struct osd_bitmap *osd_create_bitmap(int width, int height)
{
	struct osd_bitmap *bitmap;

        if (Machine->orientation & ORIENTATION_SWAP_XY)
        {
                int temp;

                temp = width;
                width = height;
                height = temp;
        }

	if ((bitmap = (struct osd_bitmap *)malloc(sizeof(struct osd_bitmap) + (height-1)*sizeof(unsigned char *))) != 0)
	{
		int i;
		unsigned char *bm;


		bitmap->width = width;
		bitmap->height = height;
		if ((bm = (unsigned char *)malloc(width * height * sizeof(unsigned char))) == 0)
		{
			free(bitmap);
			return 0;
		}

		for (i = 0;i < height;i++)
			bitmap->line[i] = &bm[i * width];

		bitmap->_private = bm;

		osd_clearbitmap(bitmap);
	}
	
	return bitmap;
}

/* set the bitmap to black */
void osd_clearbitmap(struct osd_bitmap *bitmap)
{
	int i;

	
	for (i = 0;i < bitmap->height;i++)
		memset(bitmap->line[i],0,bitmap->width);
}



void osd_free_bitmap(struct osd_bitmap *bitmap)
{
	if (bitmap)
	{
		free(bitmap->_private);
		free(bitmap);
	}
}

struct osd_bitmap *osd_create_display(int width,int height,int totalcolors,
		const unsigned char *palette,unsigned char *pens,int attributes)
{
	gBitmap = osd_create_bitmap(width,height);
	gDisplay->Resize(width, height);

	/* initialize the palette */
	{
		int i;

		gDisplay->InitPalette();

		if (totalcolors < 256) {
			/* if we have free places, fill the palette starting from the end, */
			/* so we don't touch color 0, which is better left black */
			for (i = 0;i < totalcolors;i++)
				pens[i] = 255-i;
		}
		else {
			for (i = 0;i < totalcolors;i++)
				pens[i] = i;
		}

		for (i = 0;i < totalcolors;i++)
			gDisplay->SetPen(pens[i], palette[3*i], palette[3*i+1], palette[3*i+2]);
	}
	
	return gBitmap;
}

void osd_close_display(void)
{
}

void osd_modify_pen(int pen,unsigned char red, unsigned char green, unsigned char blue)
{
	gDisplay->SetPen(pen, red, green, blue);
}

void osd_get_pen(int pen,unsigned char *red, unsigned char *green, unsigned char *blue)
{
	gDisplay->GetPen(pen, red, green, blue);
}

void osd_mark_dirty(int x1, int y1, int x2, int y2, int ui)
{
}

void osd_update_display(void)
{
	gDisplay->CopyBits(gBitmap->_private, gBitmap->height, gBitmap->width);
}

void osd_update_audio(void)
{
}

void osd_play_sample(int channel,unsigned char *data,int len,int freq,int volume,int loop)
{
	if (gAudio != NULL)
		gAudio->PlaySample(channel, data, len, freq, volume, loop);
}

void osd_play_streamed_sample(int channel,unsigned char *data,int len,int freq,int volume)
{
	if (gAudio != NULL)
		gAudio->PlayStreamedSample(channel, data, len, freq, volume);
}

void osd_adjust_sample(int channel,int freq,int volume)
{
	if (gAudio != NULL)
		gAudio->AdjustSample(channel, freq, volume);
}

void osd_stop_sample(int channel)
{
	if (gAudio != NULL)
		gAudio->StopSample(channel);
}

void osd_restart_sample(int channel)
{
	if (gAudio != NULL)
		gAudio->RestartSample(channel);
}

int osd_get_sample_status(int channel)
{
}

void osd_ym2203_write(int n, int r, int v)
{
}

void osd_ym2203_update(void)
{
}

void osd_set_mastervolume(int volume)
{
	if (gAudio != NULL)
		gAudio->SetMasterVolume(volume);
}

int osd_key_pressed(int keycode)
{
	return gOSDKeys[keycode];
}

int osd_read_key(void)
{
	int	key;

	/* wait for all keys to be released */
	do {
		for (key = OSD_MAX_KEY;key >= 0;key--)
				if (osd_key_pressed(key)) break;
	} while (key >= 0);
	
	/* wait for a key press */
	do {
		for (key = OSD_MAX_KEY;key >= 0;key--)
			if (osd_key_pressed(key)) break;
		snooze(100.0);
	} while (key < 0);

	return key;
}

int osd_read_keyrepeat(void)
{
	CIOThread::ClearKeys();
	return CIOThread::GetKey();
}

const char *osd_joy_name(int joycode)
{
}

const char *osd_key_name(int keycode)
{
  static char *keynames[] = {	"ESC", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
  							"F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
  							"TILDE", "1", "2", "3", "4", "5", "6", "7",
  							"8", "9", "0", "MINUS", "EQUAL", "BACKSPACE", "INS", "HOME",
  							"PGUP", "NUMLOCK", "DIV PAD", "ASTERISK", "MINUS PAD", "TAB", "Q", "W",
							"E", "R", "T", "Y", "U", "I", "O", "P",
							"OPENBRACE", "CLOSEBRACE", "BSLASH", "DEL", "END", "PGDN", "7 PAD", "8 PAD", "9 PAD",
							"PLUS PAD", "CAPSLOCK", "A", "S", "D", "F", "G", "H",
							"J", "K", "L", "COLON", "QUOTE", "RETURN", "4 PAD", "5 PAD",
							"6 PAD", "LEFTSHIFT", "Z", "X", "C", "V", "B", "N",
							"M", "COMMA", "STOP", "SLASH", "RIGHTSHIFT", "UP", "1 PAD", "2 PAD",
							"3 PAD", "ENTER PAD", "CONTROL", "ALT", "SPACE", "ALTR", "CONTROLR", "LEFT",
							 "DOWN", "RIGHT", "0 PAD", "STOP PAD", "COMMAND"  };

  if (keycode && keycode <= OSD_MAX_KEY) return (char*)keynames[keycode-1];
  	else return 0;
}

void osd_poll_joystick(void)
{
	if (gJoystick != NULL) {
		gJoystick->Update();
		
		if (gJoystick->horizontal > gJoystickState.hCenter + gJoystickState.hSlop)
			gJoystickState.left = 1;
		else
			gJoystickState.left = 0;
			
		if (gJoystick->horizontal < gJoystickState.hCenter - gJoystickState.hSlop)
			gJoystickState.right = 1;
		else
			gJoystickState.right = 0;
			
		if (gJoystick->vertical > gJoystickState.vCenter + gJoystickState.vSlop)
			gJoystickState.up = 1;
		else
			gJoystickState.up = 0;
			
		if (gJoystick->vertical < gJoystickState.vCenter - gJoystickState.vSlop)
			gJoystickState.down = 1;
		else
			gJoystickState.down = 0;
			
		if (gJoystick->button1 == FALSE)
			gJoystickState.button1 = 1;
		else
			gJoystickState.button1 = 0;
			
		if (gJoystick->button2 == FALSE)
			gJoystickState.button2 = 1;
		else
			gJoystickState.button2 = 0;
	}
}

int osd_joy_pressed(int joycode)
{
	if (gJoystick != NULL)
	{
		switch(joycode)
		{
			case OSD_JOY_LEFT:
				return gJoystickState.left;
				break;
				
			case OSD_JOY_RIGHT:
				return gJoystickState.right;
				break;
				
			case OSD_JOY_UP:
				return gJoystickState.up;
				break;
				
			case OSD_JOY_DOWN:
				return gJoystickState.down;
				break;
				
			case OSD_JOY_FIRE1:
				return gJoystickState.button1;
				break;
			
			case OSD_JOY_FIRE2:
				return gJoystickState.button2;
				break;
			
			case OSD_JOY_FIRE3:
				return 0;	// simulate in future?
				break;
			
			case OSD_JOY_FIRE4:
				return 0;	// simulate in future?
				break;
				
			case OSD_JOY_FIRE:
				return (gJoystickState.button1 > 0 || gJoystickState.button2 > 0);
				break;
				
			default:
				return 0;
				break;
		}
	}
	else
		return 0;
}


int osd_trak_read(int axis)
{
	static int16	deltaX, deltaY;
	int16		posX=0, posY=0;
	int16		ret;

	if (!gUseMouse)
		return 0;

	gDisplay->ReadMouse(&posX, &posY);

	deltaX += posX;
	deltaY += posY;

	switch(axis) {
		case X_AXIS:
			ret=deltaX;
			deltaX=0;
			break;
		case Y_AXIS:
			ret=deltaY;
			deltaY=0;
			break;
		default:
			ret=0;
			if (errorlog)
				fprintf (errorlog, "Error: no axis in osd_track_read\n");
	}

	return ret;
}

#define MAXPIXELS 100000
uchar * pixel[MAXPIXELS];
int p_index=-1;

inline void osd_draw_pixel (int x, int y, int col)
{
	uchar *address;


	if (x<0 || x >= gBitmap->width)
		return;
	if (y<0 || y >= gBitmap->height)
		return;
	address=&(gBitmap->line[y][x]);
	*address=(uchar)col;
	if (p_index<MAXPIXELS-1) {
		p_index++;
		pixel[p_index]=address;
	}
}

int osd_update_vectors(int *x_res, int *y_res, int step)
{
	int i;
	unsigned char bg;

	if (Machine->orientation & ORIENTATION_SWAP_XY)
	{
		*y_res=gBitmap->width;
		*x_res=gBitmap->height;
	}
	else
	{
		*x_res=gBitmap->width;
		*y_res=gBitmap->height;
	}

	/* Clear the old bitmap. Delete pixel for pixel, this is
           faster than memset. */
	bg=Machine->pens[0];
	for (i=p_index; i>=0; i--)
	{
		*(pixel[i])=bg;
	}
	p_index=-1;

	return 0;
}

void osd_draw_to(int x2, int y2, int col)
{
	int orientation;
	int dx,dy,cx,cy,sx,sy;
	static int x1,y1;

	orientation = Machine->orientation;

	if (orientation != ORIENTATION_DEFAULT)
	{
		if (orientation & ORIENTATION_SWAP_XY)
		{
			int temp;
			temp = x2;
			x2 = y2;
			y2 = temp;
		}
		if (orientation & ORIENTATION_FLIP_X)
			x2 = gBitmap->width-1-x2;
		if (orientation & ORIENTATION_FLIP_Y)
			y2 = gBitmap->height-1-y2;
	}

	if (col<0)
	{
		x1=x2;
		y1=y2;
		return;
	} else
		col=Machine->gfx[0]->colortable[col];


	dx=abs(x1-x2);
	dy=abs(y1-y2);

	sx = ((x1 <= x2) ? 1 : -1);
	sy = ((y1 <= y2) ? 1 : -1);
	cx=dx/2;
	cy=dy/2;

	if (dx>=dy)
	{
		for (;;)
		{
			osd_draw_pixel(x1,y1,col);
			if (x1 == x2) break;
			x1+=sx;
			cx-=dy;
			if (cx < 0)
			{
				y1+=sy;
				cx+=dx;
			}
		 }
	}
	else
	{
		for (;;)
		{
			osd_draw_pixel(x1,y1,col);
			if (y1 == y2) break;
			y1+=sy;
			cy-=dx;
			if (cy < 0)
			{
				x1+=sx;
				cy+=dy;
			}
		}
	}
}

void *osd_fopen (const char *gamename,const char *filename,int filetype,int write)
{
	status_t		theStatus = B_NO_ERROR;
	BEntry		appEntry;
	BDirectory	theDir;
	char			name[B_FILE_NAME_LENGTH];
	BFile		*theFile = NULL;
	
	appEntry.SetTo(&gAppInfo.ref);
	appEntry.GetParent(&appEntry);
	theDir.SetTo(&appEntry);
	
	switch (filetype) {
		case OSD_FILETYPE_ROM:
			if ((theStatus = theDir.SetTo(&theDir, "ROMS")) == B_NO_ERROR) {
				if ((theStatus = theDir.SetTo(&theDir, gamename)) == B_NO_ERROR) {
					BEntry	romEntry;

					theDir.Rewind();
					while ((theStatus = theDir.GetNextEntry(&romEntry)) == B_NO_ERROR) {
						romEntry.GetName(name);
						if (!strcasecmp(name, filename))
							break;
					}
					if (theStatus == B_NO_ERROR)
						theFile = new BFile(&romEntry, write ? B_WRITE_ONLY : B_READ_ONLY);
				}
			}
			break;
			
		case OSD_FILETYPE_SAMPLE:
			if ((theStatus = theDir.SetTo(&theDir, "Samples")) == B_NO_ERROR) {
				if ((theStatus = theDir.SetTo(&theDir, gamename)) == B_NO_ERROR) {
					BEntry	sampEntry;

					theDir.Rewind();
					while ((theStatus = theDir.GetNextEntry(&sampEntry)) == B_NO_ERROR) {
						sampEntry.GetName(name);
						if (!strcasecmp(name, filename))
							break;
					}
					if (theStatus == B_NO_ERROR)
						theFile = new BFile(&sampEntry, write ? B_WRITE_ONLY : B_READ_ONLY);
					else
						printf("could not open %s\n", filename);
				}
				else
					printf("Dir %s not found\n", gamename);
			}
			break;
			
		case OSD_FILETYPE_HIGHSCORE:
			if ((theStatus = theDir.SetTo(&theDir, "Highscores")) == B_NO_ERROR) {
				char		hiName[B_FILE_NAME_LENGTH];

				sprintf(hiName, "%s.hi", gamename);
				theFile = new BFile();
				theStatus = theFile->SetTo(&theDir, hiName, (write ? B_WRITE_ONLY : B_READ_ONLY) | B_CREATE_FILE);
				if (theStatus != B_NO_ERROR) {
					delete theFile;
					theFile = NULL;
				}
			}
			break;
			
		case OSD_FILETYPE_CONFIG:
			if ((theStatus = theDir.SetTo(&theDir, "Settings")) == B_NO_ERROR) {
				char		cfgName[B_FILE_NAME_LENGTH];

				sprintf(cfgName, "%s.cfg", gamename);
				theFile = new BFile();
				theStatus = theFile->SetTo(&theDir, cfgName, (write ? B_WRITE_ONLY : B_READ_ONLY) | B_CREATE_FILE);
				if (theStatus != B_NO_ERROR) {
					delete theFile;
					theFile = NULL;
				}
			}
			break;
	}
	return theFile;
}

int osd_fread (void *file,void *buffer,int length)
{
	BFile	*theFile = (BFile *)file;

	return theFile->Read(buffer, length);
}

int osd_fwrite (void *file,const void *buffer,int length)
{
	BFile	*theFile = (BFile *)file;

	return theFile->Write(buffer, length);
}

int osd_fseek (void *file,int offset,int whence)
{
	BFile	*theFile = (BFile *)file;
	
	theFile->Seek(offset, whence);
}

void osd_fclose (void *file)
{
	BFile	*theFile = (BFile *)file;
	
	delete theFile;
}

void osd_led_w(int led,int on)
{
}

char *create_cheat_path()
{
	char			*cheatPath = NULL;
	status_t		theStatus = B_NO_ERROR;
	BEntry		appEntry;
	BDirectory	appDir;
	
	if ((theStatus = appEntry.SetTo(&gAppInfo.ref)) == B_NO_ERROR) {
		if ((theStatus = appEntry.GetParent(&appDir)) == B_NO_ERROR) {
			BPath	appPath(&appDir, NULL);
			
			cheatPath = (char *)::malloc(strlen(appPath.Path()) + strlen("/cheat.dat") + 1);
			sprintf(cheatPath, "%s/cheat.dat", appPath.Path(), "/cheat.dat");
		}
	}
	
	return cheatPath;
}

void center_joystick()
{
	if (gJoystick != NULL)
	{
		int32	hTotal, vTotal;
		int16	hMaxDeviation, vMaxDeviation;
		int8	i;
		
		hTotal = vTotal = 0;
		hMaxDeviation = vMaxDeviation = 0;
		
		//  Poll the joystick 25 times (a healthy amount).
		//  Keep track of the most fluctuation on each axis,
		//  and set the dead range accordingly.  Determine the
		//  center position of the joystick, and set that.
		
		for (i = 0; i < 24; i++)
		{
			gJoystick->Update();
			if (gJoystick->horizontal > hMaxDeviation)
				hMaxDeviation = gJoystick->horizontal;
			if (gJoystick->vertical > vMaxDeviation)
				vMaxDeviation = gJoystick->vertical;
			hTotal += gJoystick->horizontal;
			vTotal += gJoystick->vertical;
		}
		gJoystickState.hCenter = hTotal / 25;
		gJoystickState.vCenter = vTotal / 25;
		gJoystickState.hSlop = (hMaxDeviation - gJoystickState.hCenter) * 2;
		gJoystickState.vSlop = (vMaxDeviation - gJoystickState.vCenter) * 2;
	}
}
