/*
	CMameDirectWin.cc
	The BeMAME Team
	Created: 07/16/99 14:38:19
*/


#include "CMameDirectWin.h"
#include "CMameView.h"
#include <Application.h>
#include <Screen.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>

CMameDirectWin::CMameDirectWin(
	status_t		*outRet,
	BRect			inFrame,
	int				inColorDepth) : BDirectWindow(inFrame, "MAME", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE)
{
	BScreen		theScreen;
	BRect		aFrame = theScreen.Frame();

	mValid = false;
	
	MoveTo((aFrame.Width()-inFrame.Width())/2, (aFrame.Height()-inFrame.Height())/2);

	aFrame = Bounds();
	AddChild(mView = new CMameView(Bounds()));
	mView->MakeFocus();

	fClipList = NULL; 
	fInfoList = NULL;
	fNumClipRects = 0; 
	SetWindowAlignment(B_BYTE_ALIGNMENT, 4, 0, 4);

	mRequestedSize = inFrame;
puts("CMameDirectWin::CMameDirectWin()");
	Show();
}

CMameDirectWin::~CMameDirectWin()
{
	mLocker.Lock();			// wait for any drawing to finish
	mValid = false;
	mLocker.Unlock();		// can't lock here, or else mame thread could deadlock
	free(fClipList);
	free(fInfoList);puts("CMameDirectWin::~CMameDirectWin()");
}

bool
CMameDirectWin::QuitRequested()
{
	/*Hide();*/ puts("CMameDirectWin::QuitRequested()");
	WaitToQuit();
puts("CMameDirectWin::QuitRequested() done");
	return true;
}

void
CMameDirectWin::DirectConnected(
	direct_buffer_info		*inInfo)
{
	switch (inInfo->buffer_state & B_DIRECT_MODE_MASK) {
		case B_DIRECT_START:
			mLocker.Lock();
			SetInfo(inInfo);
			mValid = true;
			mLocker.Unlock();
			break;		// don't fall through here, mame thread could be blocked on mDrawLockID
						// releasing prematurely will cause mame (drawing) thread to use bogus info
			
		case B_DIRECT_MODIFY :
			mLocker.Lock();
			SetInfo(inInfo);
			mLocker.Unlock();
			break;
			
		case B_DIRECT_STOP :
			mLocker.Lock();			// wait for any drawing to finish
			mValid = false;
			mLocker.Unlock();
			break;
	}
}

void
CMameDirectWin::ShutDown()
{
	PostMessage(B_QUIT_REQUESTED);
}

void
CMameDirectWin::SetInfo(
	direct_buffer_info		*inInfo)
{
	fBits = (uint8 *) inInfo->bits; 
	fRowBytes = inInfo->bytes_per_row; 
	fFormat = inInfo->pixel_format; 
	switch(fFormat) //determine our offsets and whatnot
		{
		case B_CMAP8:depth=1;break;
		case B_RGB15:depth=2;break;
		case B_RGB16:depth=2;break;
		case B_RGB32:depth=4;break;				
		};
	fBounds = inInfo->window_bounds; 
	
	// Get clipping information 
	
	if (fClipList) { 
	   free(fClipList); 
	   fClipList = NULL; 
	} 
	fNumClipRects = inInfo->clip_list_count; 
	fClipList = (clipping_rect *)malloc(fNumClipRects*sizeof(clipping_rect)); 
	memcpy(fClipList, inInfo->clip_list, fNumClipRects*sizeof(clipping_rect));     
	
	if (fInfoList) { 
	   free(fInfoList); 
	   fInfoList = NULL; 
	} 
fInfoList = (infobits *)malloc(fNumClipRects*sizeof(infobits)); 
	
int i=0;
while (i<fNumClipRects)
	{
	clipping_rect *clip = &(fClipList[i]);	//get the current clip rect
	infobits *info=&(fInfoList[i++]);
	info->needsx=0;
	info->needsy=0;
	//determine left/right offset
	if (clip->left>fBounds.left)
	{
	info->offsetx=clip->left-fBounds.left;
	info->needsx=1;
	}
								
	//determine top bottom offset
	if (clip->top>fBounds.top)
	{
	info->offsety=clip->top-fBounds.top;
	info->needsy=1;
	}
						
	info->rowsize=(clip->right - clip->left)+1;
	info->height = (clip->bottom - clip->top)+1; 

	info->p = fBits+(clip->top*fRowBytes)+clip->left*depth; 
	}
}

void
CMameDirectWin::SetPenAtIdx(
	int32			inIdx,
	uint8			inRed,
	uint8			inGreen,
	uint8			inBlue)
{

	BScreen scr(this);

	palette8[inIdx] = scr.IndexForColor(inRed, inGreen, inBlue);
	palette15[inIdx]=( ((inRed & 0xf8) << 7) | 
                  ((inGreen & 0xf8) << 2) | 
                  ((inBlue & 0xf8) >> 3) );
	palette16[inIdx]=( ((inRed & 0xf8) << 8) | 
                  ((inGreen & 0xf8) << 3) | 
                  ((inBlue & 0xf8) >> 3) );

	palette32[inIdx].red=inRed;
	palette32[inIdx].blue=inBlue;
	palette32[inIdx].green=inGreen;

}

void CMameDirectWin::SetPalette()
{
}

void
CMameDirectWin::Update8BitDirect(
	uint8			*inSource,
	int32			inLineNumber,
	int32			inScreenWidth,
	int32			inVisibleWidth,
	bool			inDouble)
{
	// Fill in values for vector games...
	if (inVisibleWidth == 0) {
		inVisibleWidth = mRequestedSize.right + 1;
	}
	if (inScreenWidth == 0) {
		inScreenWidth = mRequestedSize.right + 1;
	}

	if (inDouble)
		Update8BitDoubleDirect(inSource,inLineNumber,inScreenWidth,inVisibleWidth);
	else
		Update8BitSingleDirect(inSource,inLineNumber,inScreenWidth,inVisibleWidth);
}

void
CMameDirectWin::Update8BitSingleDirect(
	uint8			*inSource,
	int32			inLineNumber,
	int32			inScreenWidth,
	int32			inVisibleWidth)
{
	mLocker.Lock();
	if (mValid) {
		// draw something
				uint32 y; 
				uint32 rowsize;
				uint32 height; 
				uint8 *p; 
				//clipping_rect *clip; 
				uint8 *q;
				uint16 * t;
				bgr_color *k;
				uint32 e;
				uint32 i;
				//int offset;

				inScreenWidth += 16;

				//adder = fRowBytes;   // Stash locally for this pass
				//for (int i=0; i<fNumClipRects; i++)
				i=0;
				while (i<fNumClipRects)
						{

	                	q=inSource;

	                	if (fInfoList[i].needsx>0)q+=fInfoList[i].offsetx;
	                	if (fInfoList[i].needsy>0)q+=fInfoList[i].offsety* /*inWidth*/ inScreenWidth;
	                	rowsize=fInfoList[i].rowsize;
	                	height=fInfoList[i].height;
	                	p=fInfoList[i++].p;
	                	y = 0;	
						while (y++ < height)
									{
									e=0;									
									switch (fFormat)
									{
									case B_RGB32:
										k=(bgr_color *)p;
										while (e<rowsize){
										*k++=palette32[q[e++]];
										}
										break;
									case B_RGB16:
										t=(uint16 *)p;
										while (e<rowsize){
										*t++=palette16[q[e++]];
										}
										break;
									case B_CMAP8:
										while (e<rowsize){
										p[e] = palette8[q[e++]];
										}
										break;
									case B_RGB15:
										t=(uint16 *)p;
										while (e<rowsize){
										*t++=palette15[q[e++]];
										}
										break;
									};
									q+=/*inWidth*/ inScreenWidth;
	     	         				p+=fRowBytes; 
	                  				}//end while
						}//end for
	}
	mLocker.Unlock();
}

/*void
CMameDirectWin::Update8BitSingleDirect(
	uint8			*inSource,
	int32			inLineNumber,
	int32			inWidth)
{
	mLocker.Lock();
	if (mValid) {
		// draw something
				int32 y; 
				int32 rowsize;
				int32 height; 
				uint8 *p; 
				clipping_rect *clip; 
				uint8 *q;
				int offset;
				int depth;
				uint16 * t;
				bgr_color *k;
				int32 e;
				
				switch(fFormat) //determine our offsets and whatnot
				{
				case B_CMAP8:depth=1;break;
				case B_RGB15:depth=2;break;
				case B_RGB16:depth=2;break;
				case B_RGB32:depth=4;break;				
				};
				
				//adder = fRowBytes;   // Stash locally for this pass
				//for (int i=0; i<fNumClipRects; i++)
				int i=0;
				while (i<fNumClipRects)
						{
						q=inSource;	//get new source
						clip = &(fClipList[i++]);	//get the current clip rect
	
						//determine left/right offset
						if (clip->left>fBounds.left)
							{
							offset=clip->left-fBounds.left;
							q+=offset;
							}
							
						//determine top bottom offset
						if (clip->top>fBounds.top)
							{
							offset=clip->top-fBounds.top;
							q+=offset*inWidth;
							}
							
						rowsize=(clip->right - clip->left)+1;
						height = (clip->bottom - clip->top)+1; 
	                	p = fBits+(clip->top*fRowBytes)+clip->left*depth; 
	                	y = 0;	
						while (y < height)
									{
									e=0;									
									switch (fFormat)
									{
									case B_RGB32:
										k=(bgr_color *)p;
										while (e<rowsize){
										*k++=palette32[q[e++]];
										}
										break;
									case B_RGB16:
										t=(uint16 *)p;
										while (e<rowsize){
										*t++=palette16[q[e++]];
										}
										break;
									case B_CMAP8:
										while (e<rowsize){
										p[e] = palette8[q[e++]];
										}
										break;
									case B_RGB15:
										t=(uint16 *)p;
										while (e<rowsize){
										*t++=palette15[q[e++]];
										}
										break;
									};
									q+=inWidth;
	     	         				y++; 
	     	         				p+=fRowBytes; 
	                  				}//end while
						}//end for
	}
	mLocker.Unlock();
}*/



void
CMameDirectWin::Update8BitDoubleDirect(
	uint8			*inSource,
	int32			inLineNumber,
	int32			inScreenWidth,
	int32			inVisibleWidth)
{
	mLocker.Lock();
	if (mValid) {
		// draw something
				int32 y; 
				int32 rowsize;
				int32 height; 
				uint8 *p, p2; 
				clipping_rect *clip; 
				uint8 *q;
				int offset;
				uint16 * t;
				int g;
				bgr_color *k;
				int32 e;
				//adder = fRowBytes;   // Stash locally for this pass
				//for (int i=0; i<fNumClipRects; i++)

				inScreenWidth += 16;

				int i=0;
				while (i<fNumClipRects)
						{
						q=inSource;	//get new source
						clip = &(fClipList[i++]);	//get the current clip rect
	
						//determine left/right offset
						if (clip->left>fBounds.left)
							{
							offset=(clip->left-fBounds.left)>>1;
							q+=offset;
							}
							
						//determine top bottom offset
						if (clip->top>fBounds.top)
							{
							offset=(clip->top-fBounds.top)>>1;
							q+=offset* /*inWidth*/ inScreenWidth;
							}
							
						rowsize=((clip->right - clip->left)+1)>>1;
						int actual=(((clip->right - clip->left)+1)*depth)>>3;
						height = (clip->bottom - clip->top)+1; 
	                	p = fBits+(clip->top*fRowBytes)+clip->left*depth; 
	                	y = 0;	
						while (y < height)
									{
									if (y%2==0) //on even scanline draw double
										{
										e=0;									
										switch (fFormat)
										{
										case B_RGB32:
											k=(bgr_color *)p;
											bgr_color reg32;
											while (e<rowsize){
											reg32=palette32[q[e++]];
											*k++=reg32;
											*k++=reg32;
											}
											break;
										case B_RGB16:
											t=(uint16 *)p;
											uint16 reg16;
											while (e<rowsize){
											reg16=palette16[q[e++]];
											*t++=reg16;
											*t++=reg16;
											}
											break;
										case B_CMAP8:
											uint8 reg8;
											g=0;
											while (e<rowsize){
											reg8 = palette8[q[e++]];
											p[g++] = reg8;
											p[g++]= reg8;
											}
											break;
										case B_RGB15:
											t=(uint16 *)p;
											uint16 reg15;
											while (e<rowsize){
											reg15=palette15[q[e++]];
											*t++=reg15;
											*t++=reg15;
											}
											break;
											};
											q+= /*nWidth*/ inScreenWidth;
										}
									else //on odd scanline fill in black
										{
										int h=0;
										double * f=(double *)p;
										while (h++<actual){
											*f++=0;
											}
										}
									y++;
	     	         				p+=fRowBytes; 
	                  				}//end while
						}//end for
	}
	mLocker.Unlock();
}

	
void
CMameDirectWin::Update8BitDirectWScan(
	uint8			*inSource,
	int32			inLineNumber,
	int32			inScreenWidth,
	int32			inVisibleWidth)
{
Update8BitDoubleDirect(inSource,inLineNumber,inScreenWidth, inVisibleWidth);
}

// ----------------------------------------------------------------------
// Constructor
Benaphore::Benaphore(
	const char		*name )
{
	atom = 0;
	ben_sem = create_sem( 0, name );
}

// ----------------------------------------------------------------------
// Destructor
Benaphore::~Benaphore()
{
	long retval = delete_sem( ben_sem );
}
