#include "quakedef.h"
#include "d_local.h"

#include <Debug.h>

#include <WindowScreen.h>
#include <Application.h>
#include <View.h>

extern "C" void VID_SetPalette(unsigned char *palette);
extern "C" void CloseSplash(void);

viddef_t	vid;				// global video state

qboolean	vid_init = qfalse;			// video initialized (window does exist)
qboolean	vid_connected = qfalse;		// window shown/connected

#define BIG_RES 0

#if BIG_RES
#define	BASEWIDTH	640
#define	BASEHEIGHT	480
#else
#define	BASEWIDTH	320
#define	BASEHEIGHT	240
#endif

byte	vid_buffer[BASEWIDTH*BASEHEIGHT];
short	zbuffer[BASEWIDTH*BASEHEIGHT];
byte	surfcache[256*1024];

int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes;
byte	*VGA_pagebase;

unsigned short	d_8to16table[256];
unsigned	d_8to24table[256];

cvar_t _windowed_mouse = {"_windowed_mouse", "0", qtrue};

typedef long (*blit_hook)(long,long,long,long,long,long);
typedef long (*sync_hook)();

blit_hook v_blit;
sync_hook v_sync;

extern "C" void stretchby2( uint32 *src, uint32 src_width, uint32 src_height, uint32 src_skip,
							uint32 *dest, uint32 dest_skip);
extern "C" void stretchby2_mmx( uint32 *src, uint32 src_width, uint32 src_height, uint32 src_skip,
							uint32 *dest, uint32 dest_skip);

typedef enum {STRETCH_DETECT = 0, STRETCH_CPU, STRETCH_MMX, COPY_BLIT, COPY_CPU} STRETCH_METHOD;

STRETCH_METHOD stretch_method = STRETCH_DETECT;

class QWindow : public BWindowScreen
{
public:
			QWindow(status_t *error);

	virtual void ScreenConnected(bool connected);
	virtual	bool QuitRequested(void);

// --------------
	bool	vid_can_control_framebuffer;
	sem_id	vid_directaccess_sem;
	char	*fbuf;
	int		fbuf_bytes_per_row;
	int		fbuf_width;
	int		fbuf_height;

	// the next two entries are only valid if vid_connected is true
	graphics_card_info	*card_info;
	frame_buffer_info	*fbuf_info;
};

rgb_color vid_palette[256];
QWindow *qwin;
BView	*qview;	// dummy for calling GetMouse()

void copy_qpal_to_rgbpal(rgb_color *dest, const unsigned char *src, int num)
{
	int x;
	for(x=0;x<num;x++)
	{
		dest[x].red		= *src++;
		dest[x].green	= *src++;
		dest[x].blue	= *src++;
		dest[x].alpha	= 255;
	}
}

QWindow::QWindow(status_t *error)
	: BWindowScreen("QuakeWorld", B_8_BIT_640x480, error, false),
	  vid_directaccess_sem(-1),
	  card_info(NULL),
	  fbuf_info(NULL)
{
	if(*error != B_OK)
	{
		PostMessage(B_QUIT_REQUESTED);
		return;
	}
//	filter = new BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, filter_func);
//	AddCommonFilter(filter);
	vid_directaccess_sem = create_sem(0, "qw vid_directaccess");
	qview = new BView(BRect(), "dummyview", B_FOLLOW_ALL_SIDES, 0);
	if(qview != NULL)
		AddChild(qview);
	Show();
}

bool QWindow::QuitRequested(void)
{
	Disconnect();
	if(vid_directaccess_sem>=0)
	{
		delete_sem(vid_directaccess_sem);
		vid_directaccess_sem = -1;
	}
	return true;
}

#include <PushGameSound.h>
extern BPushGameSound *gs;
bool	snd_muted = false;
float	old_gain = 1.0;

void QWindow::ScreenConnected(bool connected)
{
	if(connected == true)
	{
		// connected
		
		if(snd_muted && gs)
		{
			gs->SetGain(old_gain);
			snd_muted = false;
		}

		v_blit=(blit_hook)CardHookAt(7);
		v_sync=(sync_hook)CardHookAt(10);

		card_info = CardInfo();
		vid_can_control_framebuffer = CanControlFrameBuffer();
		if(vid_can_control_framebuffer)
		{
			SetFrameBuffer(640, 480*2);
			MoveDisplayArea(0,0);
			fbuf_info = FrameBufferInfo();
			fbuf_bytes_per_row = fbuf_info->bytes_per_row;
			fbuf_width = fbuf_info->display_width;
			fbuf_height = fbuf_info->display_height;
		}
		else
		{
			fbuf_width = card_info->width;
			fbuf_height = card_info->height;
			fbuf_bytes_per_row = card_info->bytes_per_row;
		}
		fbuf = (char*)card_info->frame_buffer;
		memset(fbuf, 0, fbuf_height*fbuf_bytes_per_row);

		SetColorList(vid_palette);
//		AddCommonFilter(filter);
		vid_connected = qtrue;
		release_sem(vid_directaccess_sem);
	}
	else
	{
		// disconnected
		
		if(gs)
		{
			snd_muted = true;
			old_gain = gs->Gain();
			gs->SetGain(0.0);
		}

		if(acquire_sem(vid_directaccess_sem)==B_OK)
		{
			v_blit = NULL;
			v_sync = NULL;
			vid_connected = qfalse;
			vid_can_control_framebuffer = false;
//			RemoveCommonFilter(filter);
			card_info = NULL;
			fbuf_info = NULL;
			fbuf = NULL;
		}
	}
}

void	VID_SetPalette (unsigned char *palette)
{
	copy_qpal_to_rgbpal(vid_palette, palette, 256);
	if(vid_connected)
	{
		if(acquire_sem(qwin->vid_directaccess_sem) == B_OK)
		{
			qwin->SetColorList(vid_palette);
			release_sem(qwin->vid_directaccess_sem);
		}
	}
}

void	VID_ShiftPalette (unsigned char *palette)
{
	VID_SetPalette(palette);
}

STRETCH_METHOD DetectStretchMethod()
{
	system_info info;
	if(B_OK == get_system_info(&info))
	{
		switch(info.cpu_type)	// cpu_clock_speed
		{
			case B_CPU_INTEL_PENTIUM_MMX:
			case B_CPU_INTEL_PENTIUM_MMX_MODEL_8:
			case B_CPU_INTEL_PENTIUM_II:
			case B_CPU_INTEL_PENTIUM_II_MODEL_5:
			case B_CPU_INTEL_CELERON:
			case B_CPU_INTEL_PENTIUM_III:
				return STRETCH_MMX;
			default:
				if(info.cpu_clock_speed > 198*1000000LL)
					return STRETCH_CPU;
				else
					return COPY_CPU;
				break;
		}
	}
	return STRETCH_CPU;
}

void	VID_Init (unsigned char *palette)
{
	status_t error;

	CloseSplash();

	S_Init();	// sound gets initialized here

	vid_menudrawfn	= NULL;
	vid_menukeyfn	= NULL;

	if( COM_CheckParm("-stretchmmx") )
		stretch_method = STRETCH_MMX;
	else if( COM_CheckParm("-stretchcpu") )
		stretch_method = STRETCH_CPU;
	else if( COM_CheckParm("-nostretch") )
		stretch_method = COPY_CPU;
	else
		stretch_method = DetectStretchMethod();


	copy_qpal_to_rgbpal(vid_palette, palette, 256);

	qwin = new QWindow(&error);
	if(error != B_OK)
	{
		be_app->PostMessage(B_QUIT_REQUESTED);
		return;
	}
	vid.maxwarpwidth = WARP_WIDTH;
	vid.width = vid.conwidth = BASEWIDTH;
	vid.maxwarpheight = WARP_HEIGHT;
	vid.height = vid.conheight = BASEHEIGHT;
	vid.aspect = 1.0;
	vid.numpages = 1;
	vid.colormap = host_colormap;
	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
	vid.buffer = vid.conbuffer = vid_buffer;
	vid.rowbytes = vid.conrowbytes = BASEWIDTH;

	VGA_pagebase = vid_buffer;
	VGA_bufferrowbytes = VGA_rowbytes = VGA_width = BASEWIDTH;
	VGA_height = BASEHEIGHT;

	d_pzbuffer = zbuffer;
	D_InitCaches (surfcache, sizeof(surfcache));
}

void	VID_Shutdown (void)
{
	if(qwin)
		if(qwin->Lock())
			qwin->Quit();
}

void	VID_Update (vrect_t *rects)
{
	if(vid_connected==qfalse) return;
	if(acquire_sem(qwin->vid_directaccess_sem) == B_OK)
	{
		switch(stretch_method)
		{
			case STRETCH_MMX:
				stretchby2_mmx((uint32*)vid_buffer, vid.width, vid.height, (vid.rowbytes-vid.width),
						   (uint32*)qwin->fbuf, qwin->fbuf_bytes_per_row-(vid.width*2));
				break;
			case STRETCH_CPU:
				stretchby2((uint32*)vid_buffer, vid.width, vid.height, (vid.rowbytes-vid.width),
						   (uint32*)qwin->fbuf, qwin->fbuf_bytes_per_row-(vid.width*2));
				break;
//			case COPY_BLIT:
//				if(v_sync) v_sync();
//				//     x1,  y1,  x2,  y1    dx,  dy
//				v_blit(0,  480,  0,    0 , qwin->fbuf_bytes_per_row-1, 480-1);
//				if(v_sync) v_sync();
//				break;
			case COPY_CPU:
				for(unsigned int y=0;y<vid.height;y++)
					memcpy(qwin->fbuf+((y+(vid.height>>1))*qwin->fbuf_bytes_per_row)+(qwin->fbuf_width>>2), vid_buffer+(y*vid.rowbytes), vid.width);
				break;
			default:
				stretch_method = DetectStretchMethod();
				break;
		}
		release_sem(qwin->vid_directaccess_sem);
	}
}

/*
================
D_BeginDirectRect
================
*/
void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
{
}


/*
================
D_EndDirectRect
================
*/
void D_EndDirectRect (int x, int y, int width, int height)
{
}

void VID_LockBuffer (void)
{
	if(qwin==NULL) return;
	acquire_sem(qwin->vid_directaccess_sem);
}

void VID_UnlockBuffer (void)
{
	if(qwin==NULL) return;
	release_sem(qwin->vid_directaccess_sem);
}
