//___ my_sel.cc _______________________________________________________________
//
#include <StorageKit.h>
#include <Joystick.h>
#include <AppKit.h>
#include <String.h>
#include <stdio.h>
#include <ctype.h>
#include "my_sel.h"
#include "CConfig.h"
#include "my_app.h"
#include "MessageConsts.h"


extern BJoystick* 			GJoystick[];
extern char 				build_version[];

// Constants
const float			BORDER				= 5;
const BRect			WIN_RECT			= BRect(30, 30, 540, /*490*/ 548 );
const char* const	MMSG_SEL_LIST		= "SEL_LIST_NR";
const rgb_color		RGB_SELECTION		= {152, 255, 203, 255};
const rgb_color		RGB_WHITE			= { 0xff, 0xff, 0xff, 0xff };
const rgb_color		RGB_BLACK			= { 0x00, 0x00, 0x00, 0xff };
const rgb_color		RGB_BLUE			= { 0x00, 0x00, 0xe8, 0xff };
const rgb_color		RGB_RED				= { 0xe8, 0x00, 0x00, 0x00 };
const rgb_color		RGB_ELECTRIC		= { 0x60, 0xff, 0x60, 0xff };

const char* const	LNG_MEN_FILE		= "BeMAME";
const char* const	LNG_MEN_QUIT		= "Quit";
const char* const	LNG_WIN_TITLE		= "BeMAME";
const char* const	LNG_BUT_RUN			= "Run";
const char* const	LNG_TAB_GAMES		= "Games";
const char* const	LNG_BOX_SETTINGS	= "Settings";
//
const char* const	LNG_BOX_GRAPHICS	= "Graphics";
const char* const	LNG_LAB_WINDOW		= "Window";
const char* const	LNG_LAB_DOUBLE		= "Double";
const char* const	LNG_LAB_NOROTATE	= "No rotate";
const char* const	LNG_LAB_SCANLINES	= "Scanlines";
const char* const	LNG_LAB_DEPTH		= "Depth";
const char* const	LNG_LAB_ROT			= "Rotate";
const char* const	LNG_LAB_FLIP		= "Flip";
const char* const	LNG_LAB_FRAMESKIP	= "Frameskip";
//
const char* const	LNG_BOX_SOUND		= "Sound";
const char* const	LNG_LAB_SOUNDCARD	= "soundcard";
const char* const	LNG_LAB_USEFM		= "Use FM";
const char* const	LNG_LAB_SAMRATE		= "Sample Rate";
const char* const	LNG_LAB_SAMBITS		= "Sample Bits";
//
const char* const	LNG_BOX_JOYSTICK	= "Controls";
const char* const	LNG_LAB_USEMOUSE	= "Use Mouse";
const char* const	LNG_LAB_JOYSTICK0	= "Nr.1";
const char* const	LNG_LAB_JOYSTICK1	= "Nr.2";
const char* const	LNG_LAB_JOYSTICK2	= "Nr.3";
const char* const	LNG_LAB_JOYSTICK3	= "Nr.4";



extern "C" {
	void verify_printf (const char	*fmt, ...);
}

void verify_printf (
	const char	*fmt, ...)
{
	va_list			args;
	
	va_start(args, fmt);
//	vprintf(fmt, args);
	va_end(args);
}


CSelWin::CSelWin(void)
:	BWindow(WIN_RECT, B_EMPTY_STRING, B_TITLED_WINDOW, 0)
{
	BRect	bounds = Bounds();

	// Add Topview
	BView* topview = new BView(Bounds(), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES, B_ASYNCHRONOUS_CONTROLS);
	topview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	AddChild(topview);

	// Add Menubar
	AddMenuBar(topview);

	// Add Tabs
	bounds.InsetBy(BORDER, BORDER);
	bounds.top += mMenuBar->Bounds().bottom+1;

	mTabView = new BTabView(bounds, "TabView", B_WIDTH_FROM_LABEL);
	topview->AddChild(mTabView);
	mTabView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));

	bounds = BRect(mTabView->Bounds().left+BORDER, mTabView->Bounds().top+BORDER, mTabView->Bounds().right-BORDER-3, mTabView->Bounds().bottom-BORDER-mTabView->TabHeight()-3);
	mTabGames = new BView(bounds, "Games", B_FOLLOW_ALL_SIDES, B_WILL_DRAW);
	mTabView->AddTab(mTabGames);
	mTabView->TabAt(0)->SetLabel(LNG_TAB_GAMES);
	mTabView->Select(0);
	mTabGames->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));

	// Games
	CGame::Init(this);
	AddTabGames();
	mGameSel[0]->SetValue(B_CONTROL_ON);
	FillGamesList(0);

	if (gConfig.ReadPref("selFrame", B_RECT_TYPE,  0, &bounds, sizeof(bounds)) > 0) {
		MoveTo(bounds.LeftTop());
		ResizeTo(bounds.Width(), bounds.Height());
	}

	// misc
	SetTitle(LNG_WIN_TITLE);
	Show();
	
	// Audit games
	resume_thread(spawn_thread(AuditEntry, "AuditEntry", B_NORMAL_PRIORITY, this));
}

CSelWin::~CSelWin()
{
	BRect	frame = Frame();
	
	gConfig.WritePref("selFrame", B_RECT_TYPE,  0, &frame, sizeof(frame));
}

void CSelWin::AddTabGames(void)
{
	BRect	r;
	float	y, w, max_alph_w=0;

	//___ Add Run-Button ___
	r = mTabGames->Bounds();
	mButRun = new BButton(r, "Run", LNG_BUT_RUN, new BMessage(MSG_RUN_GAME), B_FOLLOW_RIGHT|B_FOLLOW_BOTTOM);
	mTabGames->AddChild(mButRun);
	mButRun->ResizeToPreferred();
	mButRun->MoveTo(r.right-mButRun->Bounds().Width(), r.bottom-mButRun->Bounds().Height());
	mButRun->SetEnabled(false);

	//___ Add Alph-Selectors ___
	y = 0;
	BMessage*	msg;
	for (int16 idx=0; idx<CGame::MAXLIST; idx++) {
		char buf[5];
		sprintf(buf, "%c", (idx==CGame::MAXLIST-1)?'*':'A'+idx);
		msg = new BMessage(MSG_SEL_LIST);
		msg->AddInt16(MMSG_SEL_LIST, idx);
		mGameSel[idx] = new BRadioButton(r, buf, buf, msg);
		mTabGames->AddChild(mGameSel[idx]);
		mGameSel[idx]->ResizeToPreferred();
		mGameSel[idx]->MoveTo(BORDER, y);
		mGameSel[idx]->SetEnabled(CGame::Cnt(idx) > 0);
		y += mGameSel[idx]->Bounds().Height()-4;
		max_alph_w = max_c(max_alph_w, mGameSel[idx]->Bounds().Width());
	}

	//___ Add Config ___
	mConf = new CConfView(mTabGames, "Config", B_FOLLOW_RIGHT|B_FOLLOW_TOP);
	mConf->MoveTo(r.right-mConf->Bounds().Width(), 0);

	// Add Info View
	r = mTabGames->Bounds();
	r.InsetBy(2, 2);
	r.right -= mConf->Bounds().Width()+2*BORDER;
	r.bottom -= BORDER;
	r.top = r.bottom - 128;
	r.left += max_alph_w+2*BORDER;
	mInfoView = new CInfoView(r);
	mTabGames->AddChild(mInfoView);

	//___ Add List ___
	r = mTabGames->Bounds();
	r.InsetBy(2, 2);
	r.right -= B_V_SCROLL_BAR_WIDTH+mConf->Bounds().Width()+2*BORDER;
	r.bottom -= B_H_SCROLL_BAR_HEIGHT+mInfoView->Bounds().Height()+3*BORDER;
	r.left += max_alph_w+2*BORDER;
	mGameList = new CGameList(r);
	mGameList->SetInvocationMessage(new BMessage(MSG_RUN_GAME));
	BScrollView* sv = new BScrollView("GameListScroller", mGameList, B_FOLLOW_ALL_SIDES, 0, true, true, B_FANCY_BORDER);
	sv->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	mTabGames->AddChild(sv);
}

bool CSelWin::FillGamesList(int32 Idx)
{
	BList		sortedlist;
	CGame*		game;
	int32		nr = -1;
	int32		idx;
	CGame*		item;
	BListItem*	litem;

	// Create new List
	while ((game=(CGame*)mGames.ItemAt(++nr)) != NULL) {
		if (game->Alph() == Idx) {
			idx = -1;
			while ((item = (CGame*) sortedlist.ItemAt(++idx)) != NULL) {
				if (strcasecmp(item->Name(), game->Name()) >=  0)  break;
			}
			sortedlist.AddItem(game, idx);
		}
	}

	// Empty old list
	while ((litem = (BListItem*) mGameList->RemoveItem(0L)) != NULL) {
		if (litem->OutlineLevel() == 1)  delete (BStringItem *) litem;
	}

	// Add to List
	idx = -1;
	while ((item = (CGame*) sortedlist.ItemAt(++idx)) != NULL) {
		mGameList->AddItem(item);
//		mGameList->AddUnder(new BStringItem(item->Archive()), item);
	}

	return !mGameList->IsEmpty();
}

int32
CSelWin::AuditEntry(
	void		*inObj)
{
	CSelWin		*window = (CSelWin *)inObj;
	
	window->AuditGames();
	
	return 0;
}

void
CSelWin::AuditGames()
{
	CGame			*aGame;
	int32			gidx = -1;
	
	decompose_rom_sample_path (".;roms", "");

	while ((aGame = (CGame*)mGames.ItemAt(++gidx)) != NULL) {
		aGame->Audit();
		if (mGameList->HasItem(aGame)) {
			mGameList->LockLooper();
			mGameList->InvalidateItem(mGameList->IndexOf(aGame));
			mGameList->UnlockLooper();
		}
	}
}

void CSelWin::AddMenuBar(BView* Parent)
{
	BMenu*		menu;
	BMenuItem*	mitem;

	mMenuBar = new BMenuBar(Parent->Bounds(), "MenuBar");

	// File
	mMenuBar->AddItem(menu = new BMenu(LNG_MEN_FILE));
	menu->AddItem(mitem = new BMenuItem(LNG_MEN_QUIT, new BMessage(B_QUIT_REQUESTED), 'Q'));
	mitem->SetTarget(be_app);

	Parent->AddChild(mMenuBar);
}


void CSelWin::MessageReceived(BMessage* Msg)
{

	switch (Msg->what) {
		case MSG_SEL_CHANGED: {
			BFont	butnFont;
			char	*strPtr;
			char	gameStr[128];
			
//			printf("selection changed\n");
			CGame * sel = mGameList->SelectedGame();
			mButRun->SetEnabled((sel != NULL));
			if (sel == NULL)
				strcpy(gameStr, LNG_BUT_RUN);
			else
				sprintf(gameStr, "%s '%s'", LNG_BUT_RUN, sel->Name());
	
			strPtr = gameStr;
			mButRun->GetFont(&butnFont);
			butnFont.GetTruncatedStrings((const char **)&strPtr, 1, B_TRUNCATE_END, mConf->Bounds().Width() - 20, &strPtr);
			mButRun->SetLabel(gameStr);
			
			BRect r = mButRun->Frame();
			mButRun->ResizeToPreferred();
			mButRun->MoveTo(r.right-mButRun->Bounds().Width(), r.bottom-mButRun->Bounds().Height());
			
			mInfoView->SetDriver(sel != NULL ? sel->DriverIdx() : -1);
			mConf->UpdateConfigItems(sel != NULL ? sel->Driver() : NULL);
		}	break;

//		case MSG_SEL_GAME: {
//			mButRun->SetEnabled(true);
//		}	break;

		case MSG_SEL_LIST: {
			int16 idx;
			if (Msg->FindInt16(MMSG_SEL_LIST, &idx) == B_OK) {
				mGameSel[idx]->SetValue(B_CONTROL_ON);
				FillGamesList(idx);
			}
		}	break;

		case MSG_RUN_GAME: {
			CGame* game = (CGame*)mGameList->ItemAt(mGameList->CurrentSelection());
			
//			while ((game != NULL) && (game->OutlineLevel() > 0)) {
//				game = (CGame*)mGameList->Superitem(game);
//			}
			if (game != NULL) {
				BMessage msg(MSGW_ARGS);
				msg.AddString(MSGN_ARGS, "./BeMame");
				msg.AddString(MSGN_ARGS, game->Archive());

				mConf->SaveConfigItems();

				// Graphics
				mConf->AddToMsg(&msg);
				mConf->SetJoysticks();
				be_app->PostMessage(&msg);
			}
		}	break;

		case MSG_LIST_INIT: {
				int32	i;
				
				for (i=0; i<CGame::MAXLIST; i++)
					mGameSel[i]->SetEnabled(CGame::Cnt(i) > 0);
			
				mGameSel[0]->SetValue(B_CONTROL_ON);
				FillGamesList(0);
			}
			break;
			
		default:
			BWindow::MessageReceived(Msg);
	}
}

bool CSelWin::QuitRequested(void)
{
	Hide();

	be_app->PostMessage(B_QUIT_REQUESTED);
	return true;
}


//##################################################################################################


CGameList::CGameList(BRect Rect)
	: BListView(Rect, "GameList", B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES),
	mSelGame(NULL)
{
}


CGame* CGameList::SelectedGame(void)
{
	CGame* game = (CGame*)ItemAt(CurrentSelection());

//	while ((game != NULL) && (game->OutlineLevel() > 0)) {
//		game = (CGame*)Superitem(game);
//	}
//	if (game != mSelGame) {
		mSelGame = game;
//	//	if (mSelGame != NULL)  Select(IndexOf(mSelGame));
//	}
	return mSelGame;
}


void CGameList::SelectionChanged(void)
{
	CGame* game = (CGame*)ItemAt(CurrentSelection());

//	if ((game != NULL) && (game->OutlineLevel() > 0)) {	// just a subitem
//		Select(IndexOf(Superitem(game)));
//		return;
//	}

	if (mSelGame != game) {								// Selection did change?!
		mSelGame = game;
		Window()->PostMessage(MSG_SEL_CHANGED);
	}
}


void CGameList::MakeEmpty(void)
{
	if (mSelGame != NULL) {
		mSelGame = NULL;
		Window()->PostMessage(MSG_SEL_CHANGED);
	}
	BListView::MakeEmpty();
}

//##################################################################################################


int32	CGame::sCnt[MAXLIST];
BFont	CGame::sFont;
BFont	CGame::sFontItalic;
float	CGame::sBaseLine;
float	CGame::sHeight;


CGame::CGame(
	int32		inIndex) : BListItem(0, false)
{
	mIndex	= inIndex;
	mAlph	= isalpha(Name()[0]) ? tolower(Name()[0])-'a' : MAXLIST-1;
//	mStatus = VerifyRomSet(inIndex, verify_printf);
	mStatus = -1;
	CGame::sCnt[mAlph]++;
}

void CGame::Init(
	CSelWin		*inWindow)
{
	BList			*gameList = inWindow->GameList();
	font_height		fheight;
	int32			idx, gIdx = -1;
	
	for (idx=0; idx<MAXLIST; idx++)
		CGame::sCnt[idx] = 0;

	while (drivers[++gIdx]) {
		gameList->AddItem(new CGame(gIdx));
	}
	
	sFont = be_bold_font;
	sFontItalic = sFont;
	sFontItalic.SetFace(B_ITALIC_FACE);
	sFont.GetHeight(&fheight);
	sHeight = max_c(0, fheight.ascent+fheight.descent+fheight.leading);
	sBaseLine = fheight.descent+1;
}

void
CGame::Audit()
{
	mStatus = VerifyRomSet(mIndex, verify_printf);
}

int32 CGame::Cnt(int8 Index)
{
	return ((Index < 0) || (Index >= MAXLIST)) ? 0 : CGame::sCnt[Index];
}

int16 CGame::DriverIdx(void) const
{
	return mIndex;
}

const GameDriver* CGame::Driver(void) const
{
	return drivers[mIndex];
}


const char* CGame::Name(void) const
{
	return Driver()->description;
}


const char* CGame::Archive(void) const
{
	return Driver()->name;
}

int8 CGame::Alph(void) const
{
	return mAlph;
}

void CGame::Update(BView* View, const BFont* Font)
{
	SetHeight(sHeight);
	SetWidth(sFont.StringWidth(Name()));
}


void CGame::DrawItem(BView* View, BRect ItemRect, bool DrawEverything)
{
	BFont		*itemFont = &sFont;
	rgb_color	strColor = RGB_BLACK;
	
	View->SetViewColor(IsSelected() ? RGB_SELECTION : RGB_WHITE);
	View->SetLowColor(IsSelected() ? RGB_SELECTION : RGB_WHITE);
	View->FillRect(ItemRect, B_SOLID_LOW);
	
	switch (mStatus) {
		case CORRECT:			strColor = RGB_BLACK;		break;
		case INCORRECT:			strColor = RGB_BLUE;		break;
		case NOTFOUND:			strColor = RGB_RED;			break;
		case CLONE_NOTFOUND:	strColor = RGB_RED;			break;
		default:				strColor = RGB_BLACK; itemFont = &sFontItalic;	break;
	}
	
	View->SetFont(itemFont);
	View->SetHighColor(strColor);
	View->DrawString(Name(), BPoint(ItemRect.left+8, ItemRect.bottom-sBaseLine));

	// Reset State
	View->SetFont(be_plain_font);
	View->SetViewColor(RGB_WHITE);
	View->SetLowColor(RGB_WHITE);
}


//##################################################################################################


CConfItem_Bool::CConfItem_Bool(const char* TheName, const char* TheLabel, const char* Arg, bool State)
:	BCheckBox(BRect(0,0,9,9), TheName, TheLabel, NULL)
{
	SetValue(State ? B_CONTROL_ON : B_CONTROL_OFF);
	mMsgString = Arg;
}


void CConfItem_Bool::AddToMsg(BMessage* Msg)
{
	if (Value() == B_CONTROL_ON)  Msg->AddString(MSGN_ARGS, mMsgString.String());
}


//##################################################################################################


CConfItem_Menu::CConfItem_Menu(const char* TheName, const char* TheLabel, const char* Items)
:	BMenuField(BRect(0,0,130,9), TheName, TheLabel, new BMenu("popup"))
{
	Menu()->SetLabelFromMarked(true);
	int32	pos;
	BString	str = Items;
	str.Append(",");
	while ((pos=str.FindFirst(",")) != -1) {
		BString	lab, cmd;
		str.CopyInto(lab, 0, pos);								// Extract item
		str.Remove(0, pos+1);
		bool marked = (lab.ByteAt(0) == '*');
		if (marked)  lab.Remove(0, 1);
		pos=lab.FindFirst("(");									// Find command
		if (pos != -1) {
			lab.CopyInto(cmd, pos+1, lab.Length()-pos-2);
			lab.Remove(pos, lab.Length()-pos);
		}
		BMenuItem* item = new BMenuItem(lab.String(), new BMessage('Chan'));	// Create Menu
		Menu()->AddItem(item);
		item->SetMarked(marked);
		item->Message()->AddString("CMD", cmd.String());
	}
	SetDivider(be_plain_font->StringWidth(TheLabel)+be_plain_font->StringWidth("w"));
}


void CConfItem_Menu::AddToMsg(BMessage* Msg)
{
	const char*	cmd;

	if (Menu()->FindMarked()->Message()->FindString("CMD", &cmd) == B_OK)  {
		BString		cstr(cmd);								// Copy commandline into a string
		BString		dstr;									// Buffer for single commnd part
		int32		pos;									// First position of space
		cstr += " ";										// Add a space at the end, so that we catch single commands also
		while ((pos = cstr.FindFirst(" ")) != B_ERROR) {	// Split all 
			if (pos == 0) {
				cstr.Remove(0, 1);							// Remove leading space
			} else {
				cstr.MoveInto(dstr, 0, pos);				// Get part of command
				Msg->AddString(MSGN_ARGS, dstr.String());	// Add part to arguments
			}
		}
	}
}


//##################################################################################################


CConfView::CConfView(BView* TheParent, const char* TheName, uint32 ResizeMode)
:	BBox(BRect(0,0,9,9), TheName, ResizeMode)
{
	BMenuField* 	mfield;
	BMenuItem*		mitem;
	font_height		fh;
	float			bold_font_height;
	float			y, w, bx_y;
	BBox			*bx_vid, *bx_snd, *bx_joy;

	mCurDriver = NULL;
	
	//___ Config Outer Box ___
	be_bold_font->GetHeight(&fh);
	bold_font_height = fh.ascent+fh.descent;
	SetLabel(mfield = new BMenuField(BRect(0,0,290,30), "menu", LNG_BOX_SETTINGS, new BMenu("popup")));
	mfield->SetFont(be_bold_font);
	//mfield->ResizeToPreferred();
	mfield->SetDivider(be_bold_font->StringWidth(LNG_BOX_SETTINGS)+be_bold_font->StringWidth("w"));
	mfield->Menu()->SetLabelFromMarked(true);
	mfield->Menu()->AddItem(mitem = new BMenuItem("General", NULL));
	mitem->SetMarked(true);
	mfield->Menu()->AddItem(mitem = new BMenuItem("Game", NULL));
	mitem->SetEnabled(false);
	TheParent->AddChild(this);

	//___ Add Config: Video ___
	bx_y=2*BORDER+bold_font_height;
	AddChild(bx_vid = new BBox(BRect(0, bx_y, 9, 9), LNG_BOX_GRAPHICS, B_FOLLOW_LEFT|B_FOLLOW_TOP));
	bx_vid->SetLabel(LNG_BOX_GRAPHICS);
	bx_vid->SetHighColor(92,92,92);
	y = bold_font_height;

	AddToBox(bx_vid, &y, mCnf_Window	= new CConfItem_Bool("Window",		LNG_LAB_WINDOW,		"-window",	false));
	AddToBox(bx_vid, &y, mCnf_Double	= new CConfItem_Bool("Stretch",		LNG_LAB_DOUBLE,		"-stretch",	false));
	AddToBox(bx_vid, &y, mCnf_Scanlines	= new CConfItem_Bool("Scanlines",	LNG_LAB_SCANLINES,	"-scanlines",false));
	AddToBox(bx_vid, &y, mCnf_NoRotate	= new CConfItem_Bool("NoRotate",	LNG_LAB_NOROTATE,	"-norotate",false));
	AddToBox(bx_vid, &y, mCnf_Depth		= new CConfItem_Menu("Depth",		LNG_LAB_DEPTH,		"*8-bit(-depth 8),16-bit(-depth 16)"));
	AddToBox(bx_vid, &y, mCnf_Rot		= new CConfItem_Menu("Rotate",		LNG_LAB_ROT,		"*None(),Left(-rol),Right(-ror)"));
	AddToBox(bx_vid, &y, mCnf_Flip		= new CConfItem_Menu("Flip",		LNG_LAB_FLIP,		"*None(),X(-flipx),Y(-flipy),X & Y(-flipx -flipy)"));
	AddToBox(bx_vid, &y, mCnf_Frameskip	= new CConfItem_Menu("Fskip",		LNG_LAB_FRAMESKIP,	"*None(),1(-frameskip 1),2(-frameskip 2),3(-frameskip 3),4(-frameskip 4),5(-frameskip 5),6(-frameskip 6),7(-frameskip 7),8(-frameskip 8),9(-frameskip 9),10(-frameskip 10)"));

	w = 0;											// Calc Dividers
	w = max_c(w, mCnf_Depth		->Divider());
	w = max_c(w, mCnf_Flip		->Divider());
	w = max_c(w, mCnf_Rot		->Divider());
	w = max_c(w, mCnf_Frameskip	->Divider());
	mCnf_Rot->SetDivider(w);						// Set Dividers
	mCnf_Flip->SetDivider(w);
	mCnf_Frameskip->SetDivider(w);
	mCnf_Depth->SetDivider(w);

	bx_vid->MoveTo(BORDER, bx_y);
	bx_y += bx_vid->Bounds().Height()+BORDER;

	//___ Add Config: Sound ___
	AddChild(bx_snd = new BBox(BRect(0, bx_y, 9, 9), LNG_BOX_SOUND, B_FOLLOW_LEFT|B_FOLLOW_TOP));
	bx_snd->SetLabel(LNG_BOX_SOUND);
	bx_snd->SetHighColor(92,92,92);
	y = bold_font_height;

	//soundcard			= int  ("soundcard",  	NULL, -1);LNG_LAB_SOUNDCARD
	AddToBox(bx_snd, &y, mCnf_UseFm		= new CConfItem_Bool("UseFm",	LNG_LAB_USEFM,		"-oplfm",		false));
	AddToBox(bx_snd, &y, mCnf_SamRate	= new CConfItem_Menu("SamRate",	LNG_LAB_SAMRATE,	"18500(-samplerate 18500),*22050(-samplerate 22050)"));
	AddToBox(bx_snd, &y, mCnf_SamBits	= new CConfItem_Menu("SamBits",	LNG_LAB_SAMBITS,	"8(-samplebits 8),*16(-samplebits 16)"));
	w = max_c(mCnf_SamBits->Divider(), mCnf_SamRate->Divider());
	mCnf_SamRate->SetDivider(w);
	mCnf_SamBits->SetDivider(w);

	bx_snd->MoveTo(BORDER, bx_y);
	bx_y += bx_snd->Bounds().Height()+BORDER;

	//___ Add Config: Joysticks ___
	BString		jstr("*None()");
	my_app*	 	app = (my_app*)be_app;
	for (int i=0; i<app->mJoystick.CountItems(); i++) {
		BString*	joyname = (BString*)app->mJoystickName.ItemAt(i);
		jstr += /*(i==0)?",*":*/",";
		jstr += joyname->String();
		jstr += "()";
	}
	AddChild(bx_joy = new BBox(BRect(0, bx_y, 9, 9), LNG_BOX_JOYSTICK, B_FOLLOW_LEFT|B_FOLLOW_TOP));
	bx_joy->SetLabel(LNG_BOX_JOYSTICK);
	bx_joy->SetHighColor(92,92,92);
	y = bold_font_height;

	AddToBox(bx_joy, &y, mCnf_UseMouse	= new CConfItem_Bool("Use mouse", LNG_LAB_USEMOUSE, "-mouse", false));
	AddToBox(bx_joy, &y, mCnf_Joy[0]	= new CConfItem_Menu("Stick1",	LNG_LAB_JOYSTICK0,	jstr.String()));
	AddToBox(bx_joy, &y, mCnf_Joy[1]	= new CConfItem_Menu("Stick2",	LNG_LAB_JOYSTICK1,	jstr.String()));
	AddToBox(bx_joy, &y, mCnf_Joy[2]	= new CConfItem_Menu("Stick3",	LNG_LAB_JOYSTICK2,	jstr.String()));
	AddToBox(bx_joy, &y, mCnf_Joy[3]	= new CConfItem_Menu("Stick4",	LNG_LAB_JOYSTICK3,	jstr.String()));

	bx_joy->MoveTo(BORDER, bx_y);
	bx_y += bx_joy->Bounds().Height()+BORDER;

	// Align
	w = max_c(max_c(bx_vid->Bounds().Width(), bx_joy->Bounds().Width()), bx_snd->Bounds().Width());
	bx_vid->ResizeTo(w, bx_vid->Bounds().Height());
	bx_snd->ResizeTo(w, bx_snd->Bounds().Height());
	bx_joy->ResizeTo(w, bx_joy->Bounds().Height());
	ResizeTo(2*BORDER+w, BORDER+bx_joy->Frame().bottom);
}


void CConfView::AddToMsg(BMessage* Msg)
{
	// Graphics
	mCnf_Window		->AddToMsg(Msg);
	mCnf_Double		->AddToMsg(Msg);
	mCnf_NoRotate	->AddToMsg(Msg);
	mCnf_Scanlines	->AddToMsg(Msg);
	mCnf_Depth		->AddToMsg(Msg);
	mCnf_Rot		->AddToMsg(Msg);
	mCnf_Flip		->AddToMsg(Msg);
	mCnf_Frameskip	->AddToMsg(Msg);
	// Sound
	mCnf_UseFm		->AddToMsg(Msg);
	mCnf_SamRate	->AddToMsg(Msg);
	mCnf_SamBits	->AddToMsg(Msg);
	// Controls
	mCnf_UseMouse	->AddToMsg(Msg);
}


void CConfView::AddToBox(BBox* Box, float* Y, BView* View)
{
	Box->AddChild(View);
	View->MoveTo(BORDER, *Y);
	View->ResizeToPreferred();
	*Y = View->Frame().bottom-1;
	Box->ResizeTo(max_c(Box->Bounds().Width(), View->Bounds().Width()+2*BORDER), *Y+BORDER);
}


void CConfView::SetJoysticks(void)
{
	my_app*	 	app = (my_app*)be_app;
	BString		selected_joystick;
	BString*	joystick;

	for(int n=0; n<4; n++) {
		selected_joystick.SetTo(mCnf_Joy[n]->Menu()->FindMarked()->Label());
		GJoystick[n] = NULL;
		for (int i=0; i<app->mJoystick.CountItems(); i++) {
			joystick = (BString*)app->mJoystickName.ItemAt(i);
			if (selected_joystick == *joystick) {
				GJoystick[n] = (BJoystick*)app->mJoystick.ItemAt(i);
				printf("Set Joystick %i to '%s'\n", n, selected_joystick.String());
				break;
			}
		}
	}
}

void
CConfView::UpdateConfigItems(
	const GameDriver		*inDriver)
{
	if (inDriver != mCurDriver) {
		const char		*gamename = inDriver != NULL ? inDriver->name : "global";
	
		SaveConfigItems();
		
		mCurDriver = inDriver;
		mCnf_Window->SetValue(gConfig.GetBool(gamename, "window", NULL, 0, false) ? true : false);
		mCnf_Double->SetValue(gConfig.GetBool(gamename, "stretch", NULL, 0, false) ? true : false);
		mCnf_Scanlines->SetValue(gConfig.GetBool(gamename, "scanlines", NULL,	0, false) ? true : false);
		mCnf_NoRotate->SetValue(gConfig.GetBool(gamename, "norotate", NULL,	0, false) ? true : false);
		mCnf_UseMouse->SetValue(gConfig.GetBool(gamename, "mouse", NULL, 0, false) ? true : false);
	}
}

void
CConfView::SaveConfigItems()
{
	const char		*gamename = mCurDriver != NULL ? mCurDriver->name : "global";
	
	gConfig.SetBool(gamename, "window", mCnf_Window->Value());
	gConfig.SetBool(gamename, "stretch", mCnf_Double->Value());
	gConfig.SetBool(gamename, "scanlines", mCnf_Scanlines->Value());
	gConfig.SetBool(gamename, "norotate", mCnf_NoRotate->Value());
	gConfig.SetBool(gamename, "mouse", mCnf_UseMouse->Value());
}

CInfoView::CInfoView(
	BRect			inFrame) : BView(inFrame, "Info", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM, B_WILL_DRAW)
{
	font_height		fontHeight;
	
	SetViewColor(RGB_BLACK);
	SetFont(be_fixed_font);
	mDriverIdx = -1;
	GetFontHeight(&fontHeight);
	mFontHeight = fontHeight.ascent + fontHeight.descent;
	mFontOffs = mFontHeight + fontHeight.leading;
}

void
CInfoView::SetDriver(
	int32			inDriverIdx)
{
	mDriverIdx = inDriverIdx;

	Invalidate();
}

void
CInfoView::Draw(
	BRect			inUpdateRect)
{
	BRect		bounds = Bounds();
	
	SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT));
	StrokeLine(bounds.LeftTop(), bounds.RightTop());
	StrokeLine(bounds.LeftTop(), bounds.LeftBottom());
	
	SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_1_TINT));
	StrokeLine(bounds.RightBottom(), bounds.RightTop());
	StrokeLine(bounds.RightBottom(), bounds.LeftBottom());
	
	if (mDriverIdx == -1)
		DrawMameVersion();
	else
		DrawDriverInfo();
}

void
CInfoView::DrawMameVersion()
{
	BRect	strRect = Bounds();
	
	strRect.InsetBy(2,2);
	strRect.top = strRect.bottom - mFontHeight;
	DrawStringJustified(build_version, strRect, RGB_ELECTRIC);
	strRect.OffsetBy(0, -mFontOffs);
	DrawStringJustified("BeMAME", strRect, RGB_ELECTRIC);
}

void
CInfoView::DrawDriverInfo()
{
	const GameDriver 	*machine = drivers[mDriverIdx];
	BRect				strRect = Bounds();
	char				strBuf[128];
	int32				i;
	
	strRect.InsetBy(2,2);
	strRect.bottom = strRect.top + mFontHeight;

	sprintf(strBuf, "%s", machine->description);
	DrawStringJustified(strBuf, strRect, RGB_ELECTRIC, eJustify_Left);

	strRect.OffsetBy(0, mFontOffs);
	sprintf(strBuf, "%s %s", machine->year, machine->manufacturer);
	DrawStringJustified(strBuf, strRect, RGB_ELECTRIC, eJustify_Left);

	strRect.OffsetBy(0, mFontOffs);
	sprintf(strBuf, "Directory name: %s", machine->name);
	DrawStringJustified(strBuf, strRect, RGB_ELECTRIC, eJustify_Left);

	strRect.OffsetBy(0, mFontOffs*2);
	DrawStringJustified("CPU:", strRect, RGB_ELECTRIC, eJustify_Left);
	
	strRect.OffsetBy(0, mFontOffs);
	i = 0;
	while (i < MAX_CPU && machine->drv->cpu[i].cpu_type)
	{
		sprintf(strBuf,"%s %d.%06d MHz",
				cputype_name(machine->drv->cpu[i].cpu_type),
				machine->drv->cpu[i].cpu_clock / 1000000,
				machine->drv->cpu[i].cpu_clock % 1000000);

		if (machine->drv->cpu[i].cpu_type & CPU_AUDIO_CPU)
			strcat(strBuf," (sound)");

		DrawStringJustified(strBuf, strRect, RGB_ELECTRIC, eJustify_Left);
		
		strRect.OffsetBy(0, mFontOffs);
		i++;
	}
	
	DrawMameVersion();
}

void
CInfoView::DrawStringJustified(
	const char		*inStr,
	BRect			inRect,
	rgb_color		inColor,
	h_justify		inJustify)
{
	BFont			viewFont;
	font_height		fontHeight;
	BPoint			strPt;
	
	GetFont(&viewFont);
	viewFont.GetHeight(&fontHeight);

	switch (inJustify) {
		case eJustify_Center:
			strPt.x = inRect.left + (inRect.Width() + 1) / 2 - viewFont.StringWidth(inStr)/2;
			break;
		case eJustify_Left:
			strPt.x = inRect.left + 4;
			break;
		case eJustify_Right:
			strPt.x = inRect.right - viewFont.StringWidth(inStr) - 4;
			break;
	}
	strPt.y = inRect.top + (inRect.Height() + 1) / 2 + fontHeight.ascent/2 - fontHeight.descent/2;
	SetHighColor(inColor);
	SetDrawingMode(B_OP_OVER);
	DrawString(inStr, strPt);
	SetDrawingMode(B_OP_COPY);
}

