/*
 *robotfindskitten: A Zen simulation
 *
 *Copyright (C) 1997,2000 Leonard Richardson 
 *                        leonardr@segfault.org
 *                        http://www.crummy.com/devel/
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License as
 *   published by the Free Software Foundation; either version 2 of
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or EXISTANCE OF KITTEN.  See the GNU General
 *   Public License for more details.
 *
 *   http://www.gnu.org/copyleft/gpl.html
 *
 */
 
/*
 *  Original libdream port by drw <douglas@min.net> 2001
 * 	Updated KOS 2.x port by Indiket - Dreamcast.es 2013
 */

#ifdef DREAMCAST
	#include <kos.h>
	#include "pal.h" // 50/60Hz Selector
	KOS_INIT_FLAGS(INIT_DEFAULT);
	maple_device_t *mcont;
	cont_state_t *cond;
#endif

#include "messages.h"
#include "fontset.h"

#define STUFF 15
#define KITTEN "\n\nYOU FOUND KITTEN! WAY TO GO, ROBOT!"

//GCC4 - Adapted copyright symbol and newlines.
#define INSTRUCTIONS "robotfindskitten\n \
© 1997, 2000 Leonard Richardson\n\n \
SEGA DREAMCAST READAPTATION\n \
© 2013 INDIKET - DREAMCAST.ES\n\n \
Written originally for the Nerth\n \
Pork robotfindskitten contest. In\n \
this game, you are robot (#). Your\n \
job is to find kitten. This task is\n \
complicated by the existance of\n \
various things which are not kitten.\n \
Robot must touch items to determine\n \
if they are kitten or not. The game\n \
ends when robotfindskitten.\n \
Alternatively, you may end the game\n \
by hitting the start button. See the\n \
documentation for more information.\n\n \
        PRESS START BUTTON!"

unsigned long seed=0;
//No need for "struct stat"
struct
{
	int x, y, chr, Msg, IsKitten;
	unsigned short color;
} objs[STUFF];

int randnum(int limit)
{
	seed = seed * 1164525 + 1013904223;	// EVIL. anyone know how or where
	return (seed & 0x7fffffff) % limit;	// to poll the RTC in this thing?
}

unsigned short GetColor(int r, int g, int b)
{
	return ( ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0) );
}

void DrawLine(int y, int color)
{
	int z;

	for (z=0; z<320; z++)
	{
		vram_s[y*320+z] = color;
	}
}

void DrawChar(int x, int y, int offset, unsigned short color)
{
	int dx, dy;

	for (dx=0; dx<8; dx++)
	{
		for (dy=0; dy<8; dy++)
		{
			if (fontset[dy*8+dx+(64*offset)] == '.')
				vram_s[(dy+y)*320+(dx+x)] = color;
		}
	}
}

void DrawString(int x, int y, char *string, unsigned short color)
{
	int act, cur, cr = x;

	while (*string)
	{
		cur = *string;

		if (cur >= 91 && cur <= 96)
			cur = cur;
		else if (cur >= 97 && cur <= 122)
			cur -= 32;
		else if (cur >= 123)
			cur -= 26;

		act = cur - 32;

		if (cur == '\n')
		{
			x = cr;
			y += 10;
		}
		else
		{
			DrawChar(x,y,act,color);
			x += 8;
		}

		string++;
	}
}

void ShadeString(int x, int y, char *string, int white)
{
	DrawString(x+2,y+2,string,GetColor(0,0,0));
	DrawString(x+1,y+1,string,GetColor(white/4,white/4,63));
	DrawString(x,y,string,GetColor(white,white,255));
}

void FadeString(int x, int y, char *string, int min, int max, int clip)
{
	int z;

	for (z=min; z<max; z+=clip)
	{
		// Mmmm.. cpu
		ShadeString(x,y,string,z);
		vid_waitvbl();
	}

	ShadeString(x,y,string,max);
}

void PrepArea(int n)
{
	int y;

	for (y=(n+20); y<240; y++)
	{
		DrawLine(y,GetColor(0,0,255));
	}
}

void Popup(int top, int bottom)
{
	int w, y, n;

	for (n=bottom; n>top; n-=2)
	{
		w = 0;

		for (y=n; y<(n+19); y++)
		{
			w++;
			DrawLine(y,GetColor(0,0,(w*13)));
		}
		
		vid_waitvbl();
	}

	PrepArea(top);
}

void PrettyBlueLineyBackground()
{
	int z;

	for (z=0; z<240; z+=2)
	{
		DrawLine(z,GetColor(0,0,z));
		DrawLine(z+1,GetColor(0,0,z/4));
	}
}

void WrapMsgs()
{
	int pos, lastsp, col, z;

	for (z=0; z<MESSAGES; z++)
	{
		col = 0;
		lastsp = 0;
		for (pos=0; pos<(strlen(messages[z]) - 1); pos++)
		{
			if (messages[z][pos] == ' ') lastsp = pos;

			col++;
			if (col > 35 && lastsp > 0)
			{
				messages[z][lastsp] = '\n';
				col = 35 - lastsp;
				lastsp = 0;
			}
		}
	}
}

void blorp()
{
	int z;

	for (z=0; z<STUFF; z++)
	{
		DrawChar((objs[z].x+2)*8,(objs[z].y+2)*8,objs[z].chr,objs[z].color);
	}
}

void ProductPlacement()
{
	int y,z,TheKitten;

	for (z=0; z<STUFF; z++)
	{
		objs[z].x = randnum(35);
		objs[z].y = randnum(16);
		objs[z].chr = randnum(67)+1;	// no invisible kittens
		objs[z].color = GetColor(randnum(192)+63,randnum(192)+63,randnum(192)+63);
		objs[z].Msg = randnum(MESSAGES);
		objs[z].IsKitten = 0;

		for (y=0; y<z; y++)
		{
			if ((objs[y].x == objs[z].x && objs[y].y == objs[z].y) || (objs[y].Msg == objs[z].Msg) || (objs[y].chr == objs[z].chr))
				z = -1;	// nuts.
		}
	}

	TheKitten = randnum(STUFF);
	objs[TheKitten].IsKitten = 1;

	blorp();
}

int collide(int x, int y)
{
	int z;

	for (z=0; z<STUFF; z++)
	{
		if (objs[z].x == x && objs[z].y == y)
			return z;
	}

	return -1;
}

int main(void)
{
	int y, z, rx, ry, cx, cy, rc=128, ri=5, LastMsgShown;

	//KOS Standard init, asking 50/60Hz
	int dc_region, ct;
	  
	dc_region = flashrom_get_region();
	ct = vid_check_cable();
	 
	/* Prompt the user for whether to run in PAL50 or PAL60 if the flashrom says
	the Dreamcast is European and a VGA Box is not hooked up. */
	if(dc_region == FLASHROM_REGION_EUROPE && ct != CT_VGA) {
		if(pal_menu()) {
			vid_set_mode(DM_320x240_NTSC, PM_RGB565);
		}
		else {
			vid_set_mode(DM_320x240_PAL, PM_RGB565);
		}
	}
	else vid_set_mode(DM_320x240_NTSC, PM_RGB565);
	
	//Now, init Maple DC system
	mcont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
	if (!mcont) return -1;
	
	WrapMsgs();
	
	//Clear screen and show instructions
	vid_clear(0,0,0);
	PrettyBlueLineyBackground();
	ShadeString(15,15,INSTRUCTIONS,255);

	while (1)
	{
		//Read for Start button
		cond = (cont_state_t *)maple_dev_status(mcont);
		if (!cond) return -1;
		
		if (cond->buttons & CONT_START)
			break;

		seed++;
		vid_waitvbl();
	}

	//Start game!
togo:	vid_clear(0,0,0);
	LastMsgShown = -1;
	Popup(150,240);
	ProductPlacement();

	while (1)
	{
		ry = randnum(16);
		rx = randnum(35);

		if (collide(rx,ry) == -1)
		{
			cx = rx;
			cy = ry;
			break;
		}
	}

	thd_sleep(500);

	//Main loop...
	//KOS: Replace "sleep" for "thd_sleep" :)
	while (1)
	{
		cond = (cont_state_t *)maple_dev_status(mcont);
		if (!cond) break;

		if (cond->buttons & CONT_DPAD_UP)
			ry-=1;

		if (cond->buttons & CONT_DPAD_DOWN)
			ry+=1;

		if (cond->buttons & CONT_DPAD_LEFT)
			rx-=1;

		if (cond->buttons & CONT_DPAD_RIGHT)
			rx+=1;

		if (cond->buttons & CONT_START)
			break;

		if (rx != cx || ry != cy)
		{
			if (rx < 0) rx = 0;
			if (ry < 0) ry = 0;
			if (rx > 35) rx = 35;
			if (ry > 16) ry = 16;
			seed += (rx + ry);
			thd_sleep(100);
		}

		y = collide(rx,ry);

		if (y < 0)	// if no collision, commit
		{
			DrawString((cx+2)*8,(cy+2)*8,"#",GetColor(0,0,0));
			cx=rx;
			cy=ry;
		}
		else		// otherwise, retract
		{
			if (objs[y].IsKitten == 1)	// fear not, kitten
			{
				for(z=20; z<154; z++)
				{
					PrepArea(150);
					DrawChar(z,182,3,GetColor(255,255,255));
					DrawChar((320-z),182,objs[y].chr,objs[y].color);
					vid_waitvbl();
					thd_sleep(10);
				}

				FadeString(18,180,KITTEN,0,255,5);
				thd_sleep(2500);
				seed++;
				goto togo;
//				return 0;
			}
			else if (y != LastMsgShown)
			{
				PrepArea(150);
				FadeString(18,180,messages[objs[y].Msg],0,255,10);
				LastMsgShown = y;
				seed += LastMsgShown;
			}

			rx=cx;
			ry=cy;
		}

		rc += ri;
		if (rc > 250 || rc < 63) ri = -ri;
		DrawChar((cx+2)*8,(cy+2)*8,3,GetColor(rc,rc,rc));
		vid_waitvbl();
	}

	return 0;
}
