/*
 *  main_Be.h - Main program, BeOS specific stuff
 *
 *  Frodo (C) 1994-1997,2002-2004 Christian Bauer
 *
 *  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 FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <AppKit.h>
#include <StorageKit.h>
#include <Path.h>
#include <InterfaceKit.h>

#include "Version.h"


// Global variables
bool FromShell;
BEntry AppDirectory;	
BBitmap *AboutBitmap;
const BRect AboutFrame = BRect(0, 0, 383, 99);


/*
 *  Create application object and start it
 */

int main(int argc, char **argv)
{	
	Frodo *the_app;

	srand(real_time_clock());
	FromShell = (argc != 0);	// !! This doesn't work...

	the_app = new Frodo();
	if (the_app != NULL) {
		the_app->Run();
		delete the_app;
	}
	return 0;
}


/*
 *  Constructor: Initialize member variables
 */

Frodo::Frodo() : BApplication(APP_SIGNATURE), this_messenger(this)
{
	TheC64 = NULL;
	AboutBitmap = NULL;
	strcpy(prefs_path, "/boot/home/config/settings/Frodo_settings");
	prefs_showing = false;

	// Create file panels
	open_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_OPEN_SNAPSHOT_RETURNED));
	open_panel->Window()->SetTitle("Frodo: Load snapshot");
	save_panel = new BFilePanel(B_SAVE_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_SAVE_SNAPSHOT_RETURNED));
	save_panel->Window()->SetTitle("Frodo: Save snapshot");
}


/*
 *  Process command line arguments
 */

void Frodo::ArgvReceived(int32 argc, char **argv)
{
	if (argc == 2) {
		strncpy(prefs_path, argv[1], 1023);
		prefs_path[1023] = 0;
	}
}


/*
 *  Process Browser arguments
 */

void Frodo::RefsReceived(BMessage *message)
{
	// Set preferences path unless prefs editor is open or C64 is running
	if (!prefs_showing && !TheC64) {
		entry_ref the_ref;
		BEntry the_entry;

		if (message->FindRef("refs", &the_ref) == B_NO_ERROR)
			if (the_entry.SetTo(&the_ref) == B_NO_ERROR)
				if (the_entry.IsFile()) {
					BPath the_path;
					the_entry.GetPath(&the_path);
					strncpy(prefs_path, the_path.Path(), 1023);
					prefs_path[1023] = 0;
				}
	}
}


/*
 *  Arguments processed, prepare emulation and show preferences editor window
 */

void Frodo::ReadyToRun(void)
{
	// Find application directory and cwd to it
	app_info the_info;
	GetAppInfo(&the_info);
	BEntry the_file(&the_info.ref);
	the_file.GetParent(&AppDirectory);
	BPath the_path;
	AppDirectory.GetPath(&the_path);
	strncpy(AppDirPath, the_path.Path(), 1023);
	AppDirPath[1023] = 0;
	chdir(AppDirPath);

	// Set up "about" window bitmap
	AboutBitmap = new BBitmap(AboutFrame, B_COLOR_8_BIT);
	FILE *logofile = fopen("Frodo Logo", "rb");
	if (logofile != NULL) {
		fread(AboutBitmap->Bits(), 384*100, 1, logofile);
		fclose(logofile);
	}

	// Load preferences
	ThePrefs.Load(prefs_path);

	// Show preferences editor (sends MSG_STARTUP on close)
	prefs_showing = true;
	ThePrefs.ShowEditor(true, prefs_path);
}


/*
 *  Handle incoming messages
 */

void Frodo::MessageReceived(BMessage *msg)
{
	switch (msg->what) {
		case MSG_STARTUP: // Start the emulation

			// Preferences editor is not longer on screen
			prefs_showing = false;

			// Create everything
			TheC64 = new C64;

			// Load ROMs
			load_rom_files();

			// Run the 6510
			TheC64->Run();
			break;

		case MSG_PREFS:	// Show preferences editor
			if (TheC64 != NULL && !prefs_showing) {
				TheC64->Pause();
				TheC64->TheDisplay->Pause();

				Prefs *prefs = new Prefs(ThePrefs);
				prefs_showing = true;
				prefs->ShowEditor(false, prefs_path);	// Sends MSG_PREFS_DONE on close
			}
			break;

		case MSG_PREFS_DONE: { // Preferences editor closed
			Prefs *prefs;
			msg->FindPointer("prefs", (void **)&prefs);
			if (!msg->FindBool("canceled")) {
				TheC64->NewPrefs(prefs);
				ThePrefs = *prefs;
			}
			delete prefs;
			prefs_showing = false;

			TheC64->TheDisplay->Resume();
			TheC64->Resume();
			break;
		}

		case MSG_RESET:	// Reset C64
			if (TheC64 != NULL)
				TheC64->Reset();
			break;

		case MSG_NMI:	// NMI
			if (TheC64 != NULL)
				TheC64->NMI();
			break;

		case MSG_SAM:	// Invoke SAM
			if (TheC64 != NULL && !prefs_showing && FromShell) {
				TheC64->Pause();
				TheC64->TheDisplay->Pause();
				SAM(TheC64);
				TheC64->TheDisplay->Resume();
				TheC64->Resume();
			}
			break;

		case MSG_NEXTDISK:	// Insert next disk in drive 8
			if (TheC64 != NULL && !prefs_showing && strlen(ThePrefs.DrivePath[0]) > 4) {
				char str[256];
				strcpy(str, ThePrefs.DrivePath[0]);
				char *p = str + strlen(str) - 5;

				// If path matches "*.?64", increment character before the '.'
				if (p[1] == '.' && p[3] == '6' && p[4] == '4') {
					p[0]++;

					// If no such file exists, set character before the '.' to '1', 'a' or 'A'
					FILE *file;
					if ((file = fopen(str, "rb")) == NULL) {
						if (isdigit(p[0]))
							p[0] = '1';
						else if (isupper(p[0]))
							p[0] = 'A';
						else
							p[0] = 'a';
					} else
						fclose(file);

					// Set new prefs
					TheC64->Pause();
					Prefs *prefs = new Prefs(ThePrefs);
					strcpy(prefs->DrivePath[0], str);
					TheC64->NewPrefs(prefs);
					ThePrefs = *prefs;
					delete prefs;
					TheC64->Resume();
				}
			}
			break;

		case MSG_TOGGLE_1541:	// Toggle processor-level 1541 emulation
			if (TheC64 != NULL && !prefs_showing) {
				TheC64->Pause();
				Prefs *prefs = new Prefs(ThePrefs);
				prefs->Emul1541Proc = !prefs->Emul1541Proc;
				TheC64->NewPrefs(prefs);
				ThePrefs = *prefs;
				delete prefs;
				TheC64->Resume();
			}
			break;

		case MSG_OPEN_SNAPSHOT:
			if (TheC64 != NULL && !prefs_showing)
				open_panel->Show();
			break;

		case MSG_SAVE_SNAPSHOT:
			if (TheC64 != NULL && !prefs_showing)
				save_panel->Show();
			break;

		case MSG_OPEN_SNAPSHOT_RETURNED:
			if (TheC64 != NULL && !prefs_showing) {
				entry_ref the_ref;
				BEntry the_entry;
				if (msg->FindRef("refs", &the_ref) == B_NO_ERROR)
					if (the_entry.SetTo(&the_ref) == B_NO_ERROR)
						if (the_entry.IsFile()) {
							char path[1024];
							BPath the_path;
							the_entry.GetPath(&the_path);
							strncpy(path, the_path.Path(), 1023);
							path[1023] = 0;
							TheC64->Pause();
							TheC64->LoadSnapshot(path);
							TheC64->Resume();
						}
			}
			break;

		case MSG_SAVE_SNAPSHOT_RETURNED:
			if (TheC64 != NULL && !prefs_showing) {
				entry_ref the_ref;
				BEntry the_entry;
				if (msg->FindRef("directory", &the_ref) == B_NO_ERROR)
					if (the_entry.SetTo(&the_ref) == B_NO_ERROR) {
						char path[1024];
						BPath the_path;
						the_entry.GetPath(&the_path);
						strncpy(path, the_path.Path(), 1023);
						strncat(path, "/", 1023);
						strncat(path, msg->FindString("name"), 1023);
						path[1023] = 0;
						TheC64->Pause();
						TheC64->SaveSnapshot(path);
						TheC64->Resume();
					}
			}
			break;

		default:
			BApplication::MessageReceived(msg);
	}
}


/*
 *  Quit requested (either by menu or by closing the C64 display window)
 */

bool Frodo::QuitRequested(void)
{
	// Stop emulation
	if (TheC64) {
		TheC64->Quit();
		delete TheC64;
	}

	delete AboutBitmap;
	delete open_panel;
	delete save_panel;

	return BApplication::QuitRequested();
}


/*
 *  Display "about" window
 */

class AboutView : public BView {
public:
	AboutView() : BView(AboutFrame, "", B_FOLLOW_NONE, B_WILL_DRAW) {}

	virtual void AttachedToWindow(void)
	{
		SetHighColor(0, 0, 0);
	}

	virtual void Draw(BRect update)
	{
		DrawBitmap(AboutBitmap, update, update);

		SetFont(be_bold_font);
		SetDrawingMode(B_OP_OVER);
		MovePenTo(204, 20);
		DrawString(VERSION_STRING);

		SetFont(be_plain_font);
		MovePenTo(204, 40);
		DrawString("by Christian Bauer");
		MovePenTo(204, 52);
		DrawString("<Christian.Bauer@uni-mainz.de>");
		MovePenTo(204, 75);
		DrawString(B_UTF8_COPYRIGHT " Copyright 1994-1997,2002-2004");
		MovePenTo(204, 87);
		DrawString("Freely distributable.");
	}

	virtual void MouseDown(BPoint point)
	{
		Window()->PostMessage(B_QUIT_REQUESTED);
	}
};

class AboutWindow : public BWindow {
public:
	AboutWindow() : BWindow(AboutFrame, NULL, B_BORDERED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK)
	{
		Lock();
		MoveTo(100, 100);
		AboutView *view = new AboutView;
		AddChild(view);
		view->MakeFocus();
		Unlock();
		Show();
	}
};

void Frodo::AboutRequested(void)
{
	new AboutWindow();
}


/*
 *  Determine whether path name refers to a directory
 */

bool IsDirectory(const char *path)
{
	struct stat st;
	return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
}
