/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <QApplication>
#include <QDir>
#include <QPluginLoader>
#include <QMessageBox>
#include <QString>
#include <QLibrary>

#include "DebuggerCoreInterface.h"
#include "DebuggerPluginInterface.h"
#include "DebuggerMain.h"
#include "Debugger.h"
#include "version.h"

#include <iostream>
#include <ostream>

//------------------------------------------------------------------------------
// Name: loadPlugins(const QString &directory)
// Desc: attemps to load all plugins in a given directory
//------------------------------------------------------------------------------
static void loadPlugins(const QString &directory) {

	QDir pluginsDir(qApp->applicationDirPath());
	
	// TODO: attempt to detect the same plugin being loaded twice
	
	pluginsDir.cd(directory);

	foreach(QString fileName, pluginsDir.entryList(QDir::Files)) {
	
	
		if(QLibrary::isLibrary(fileName)) {
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));

			QObject *const plugin = loader.instance();

			if (plugin != 0) {

				// TODO: handle the case where we find more than one core plugin...
				DebuggerCoreInterface *const core_plugin = qobject_cast<DebuggerCoreInterface *>(plugin);
				if(core_plugin != 0) {
					if(edb::v1::debuggerBase == 0) {
						edb::v1::log("Loading Core Plugin: %-25s : %p", qPrintable(fileName), plugin);
						edb::v1::debuggerBase = core_plugin;
					}
				}

				DebuggerPluginInterface *const generic_plugin = qobject_cast<DebuggerPluginInterface *>(plugin);
				if(generic_plugin != 0) {
					if(edb::v1::registerPlugin(generic_plugin)) {
						// we have a general purpose plugin
						edb::v1::log("Loading Plugin: %-30s : %p", qPrintable(fileName), plugin);
					}
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: startDebugger(edb::pid_t attach_pid, QApplication &app)
// Desc: starts the main debugger code
//------------------------------------------------------------------------------
static int startDebugger(edb::pid_t attach_pid, const QString &program, const QStringList &programArgs, QApplication &app) {
	edb::v1::log("Starting EDB Version: %s", version_string);
	edb::v1::log("Please Report Bugs & Requests At: http://bugs.codef00.com/");

	// create the main window object	
	DebuggerMain mainWin;
	
	// look for some plugins..
	loadPlugins(QDir().absolutePath());
	loadPlugins(edb::v1::config().pluginPath);
	
	// let the plugins setup their menus
	mainWin.finishPluginSetup(edb::v1::pluginList());
	
	// ok things are initialized to a reasonable degree, let's show the main window
	mainWin.show();

	// have we been asked to attach to a given program?	
	if(attach_pid != 0) {
		mainWin.doAttach(attach_pid);
	} else if(!program.isEmpty()) {
		mainWin.doRun(program, programArgs);
	}
		
	if(edb::v1::debuggerBase == 0) {
		QMessageBox::warning(0, 
			"EDB Failed To Load A Neccessary Plugin", 
			"Failed to load debugger core plugin. Please make sure it exists and that the plugin path is correctly configured.\n"
			"This is normal if EDB has not been previously run or the configuration file has been removed.");
			
		edb::v1::dialogOptions()->exec();
		
		// TODO: detect if they corrected the issue and try again
		return -1;
	} else {
	    return app.exec();
	}
}

//------------------------------------------------------------------------------
// Name: main(int argc, char *argv[])
// Desc: entry point
//------------------------------------------------------------------------------
int main(int argc, char *argv[]) {

    QApplication app(argc, argv);
	
	srand(time(0));
	
	// setup organization info so settings go in right place
	QApplication::setOrganizationName("codef00.com");
	QApplication::setOrganizationDomain("codef00.com");
	QApplication::setApplicationName("EDB");
	
	const QStringList args = app.arguments();
	edb::pid_t attach_pid = 0;
	QStringList runArgs;
	QString runApp;

	if(args.size() > 1) {
		if(args.size() == 3 && args[1] == "--attach") {
			attach_pid = args[2].toUInt();
		} else if(args.size() >= 3 && args[1] == "--run") {
			runApp = args[2];
			
			for(int i = 3; i < args.size(); ++i) {
				runArgs.push_back(args[i]);
			}
		} else if(args.size() == 2 && args[1] == "--version") {
			std::cout << "EDB Version: " << version_string << std::endl;
			return 0;
		} else {
			std::cerr << "usage: " << qPrintable(args[0]) << " [ --attach <pid> ] [ --run <program> (args...) ] [ --version ]" << std::endl;
			return -1;
		}
	}

	return startDebugger(attach_pid, runApp, runArgs, app);
}
