/*
 * Copyright 2001-2015, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Ingo Weinhold, ingo_weinhold@gmx.de
 */

#include "Registrar.h"

#include <stdio.h>
#include <string.h>

#include <exception>

#include <Application.h>
#include <Clipboard.h>
#include <Message.h>
#include <OS.h>
#include <RegistrarDefs.h>
#include <RosterPrivate.h>

#if 0  // not-HoG_GENODE
#include "AuthenticationManager.h"
#include "ClipboardHandler.h"
#endif
#include "Debug.h"
#if 0  // not-HoG_GENODE
#include "EventQueue.h"
#include "MessageDeliverer.h"
#include "MessageEvent.h"
#include "MessageRunnerManager.h"
#include "MessagingService.h"
#include "MIMEManager.h"
#include "PackageWatchingManager.h"
#include "ShutdownProcess.h"
#else
#	include <syscalls.h>  // _kern_shutdown()... remove this once I enable ShutdownProcess
#	include <support/StringList.h>
#endif
#include "TRoster.h"


/*!
	\class Registrar
	\brief The application class of the registrar.

	Glues the registrar services together and dispatches the roster messages.
*/

#if 0  // not-HoG_GENODE
using std::nothrow;
#endif
using namespace BPrivate;

#if 0  // not-HoG_GENODE
//! Name of the event queue.
static const char *kEventQueueName = "timer_thread";

//! Time interval between two roster sanity checks (1 s).
static const bigtime_t kRosterSanityEventInterval = 1000000LL;
#endif


/*!	\brief Creates the registrar application class.
	\param error Passed to the BApplication constructor for returning an
		   error code.
*/
Registrar::Registrar(status_t* _error)
	:
	BServer(B_REGISTRAR_SIGNATURE, B_REGISTRAR_PORT_NAME, -1, false, _error),
#if 0  // not-HoG_GENODE
	fRoster(NULL),
	fClipboardHandler(NULL),
	fMIMEManager(NULL),
	fEventQueue(NULL),
	fMessageRunnerManager(NULL),
	fSanityEvent(NULL),
	fShutdownProcess(NULL),
	fAuthenticationManager(NULL),
	fPackageWatchingManager(NULL)
#else
	initPilot(),
	deployPilot(),
	fRoster(NULL)
#endif
{
	FUNCTION_START();

	set_thread_priority(find_thread(NULL), B_NORMAL_PRIORITY + 1);
}


/*!	\brief Frees all resources associated with the registrar.

	All registrar services, that haven't been shut down earlier, are
	terminated.
*/
Registrar::~Registrar()
{
	FUNCTION_START();
	Lock();
#if 0  // not-HoG_GENODE
	fEventQueue->Die();
	delete fAuthenticationManager;
	delete fPackageWatchingManager;
	delete fMessageRunnerManager;
	delete fEventQueue;
	delete fSanityEvent;
	fMIMEManager->Lock();
	fMIMEManager->Quit();
	RemoveHandler(fClipboardHandler);
	delete fClipboardHandler;
#endif
	delete fRoster;
	// Invalidate the global be_roster, so that the BApplication destructor
	// won't dead-lock when sending a message to itself.
	BRoster::Private().SetTo(BMessenger(), BMessenger());
	FUNCTION_END();
}


/*!	\brief Overrides the super class version to dispatch roster specific
		   messages.
	\param message The message to be handled
*/
void
Registrar::MessageReceived(BMessage *message)
{
	try {
		_MessageReceived(message);
	} catch (std::exception& exception) {
		char buffer[1024];
		snprintf(buffer, sizeof(buffer),
			"Registrar::MessageReceived() caught exception: %s",
			exception.what());
		debugger(buffer);
	} catch (...) {
		debugger("Registrar::MessageReceived() caught unknown exception");
	}
}


/*!	\brief Overrides the super class version to initialize the registrar
		   services.
*/
void
Registrar::ReadyToRun()
{
	FUNCTION_START();

#if 0  // not-HoG_GENODE
	// create message deliverer
	status_t error = MessageDeliverer::CreateDefault();
	if (error != B_OK) {
		FATAL("Registrar::ReadyToRun(): Failed to create the message "
			"deliverer: %s\n", strerror(error));
	}

	// create event queue
	fEventQueue = new EventQueue(kEventQueueName);

	// create authentication manager
	fAuthenticationManager = new AuthenticationManager;
	fAuthenticationManager->Init();
#endif

	// create roster
	fRoster = new TRoster;
	fRoster->Init();

#if 0  // not-HoG_GENODE
	// create clipboard handler
	fClipboardHandler = new ClipboardHandler;
	AddHandler(fClipboardHandler);

	// create MIME manager
	fMIMEManager = new MIMEManager;
	fMIMEManager->Run();

	// create message runner manager
	fMessageRunnerManager = new MessageRunnerManager(fEventQueue);

	// init the global be_roster
	BRoster::Private().SetTo(be_app_messenger, BMessenger(NULL, fMIMEManager));

	// create the messaging service
	error = MessagingService::CreateDefault();
	if (error != B_OK) {
		ERROR("Registrar::ReadyToRun(): Failed to init messaging service "
			"(that's by design when running under R5): %s\n", strerror(error));
	}

	// create the package watching manager
	fPackageWatchingManager = new PackageWatchingManager;

	// create and schedule the sanity message event
	fSanityEvent = new MessageEvent(system_time() + kRosterSanityEventInterval,
		this, B_REG_ROSTER_SANITY_EVENT);
	fSanityEvent->SetAutoDelete(false);
	fEventQueue->AddEvent(fSanityEvent);
#endif

	FUNCTION_END();
}


/*!	\brief Overrides the super class version to avoid termination of the
		   registrar until the system shutdown.
*/
bool
Registrar::QuitRequested()
{
	FUNCTION_START();
	// The final registrar must not quit. At least not that easily. ;-)
	return BApplication::QuitRequested();
}


#if 0  // not-HoG_GENODE
/*!	\brief Returns the registrar's event queue.
	\return The registrar's event queue.
*/
EventQueue*
Registrar::GetEventQueue() const
{
	return fEventQueue;
}
#endif


/*!	\brief Returns the Registrar application object.
	\return The Registrar application object.
*/
Registrar*
Registrar::App()
{
	return dynamic_cast<Registrar*>(be_app);
}


void
Registrar::_MessageReceived(BMessage *message)
{
	switch (message->what) {
#if 0  // not-HoG_GENODE
		// general requests
		case B_REG_GET_MIME_MESSENGER:
		{
			PRINT("B_REG_GET_MIME_MESSENGER\n");
			BMessenger messenger(NULL, fMIMEManager);
			BMessage reply(B_REG_SUCCESS);
			reply.AddMessenger("messenger", messenger);
			message->SendReply(&reply);
			break;
		}

		case B_REG_GET_CLIPBOARD_MESSENGER:
		{
			PRINT("B_REG_GET_CLIPBOARD_MESSENGER\n");
			BMessenger messenger(fClipboardHandler);
			BMessage reply(B_REG_SUCCESS);
			reply.AddMessenger("messenger", messenger);
			message->SendReply(&reply);
			break;
		}
#endif  // ~not-HoG_GENODE

		// shutdown process
		case B_REG_SHUT_DOWN:
		{
			PRINT("B_REG_SHUT_DOWN\n");

			_HandleShutDown(message);
			break;
		}
#if defined( HoG_GENODE )
		case B_REG_SUSPEND_TO_RAM:
		{
			PRINT("B_REG_SUSPEND_TO_RAM\n");

			_HandleSuspendToRam();
		}
		break;
		
		case B_REG_LAUNCH:
		{
			PRINT("B_REG_LAUNCH\n");

			_HandleLaunch( message );
		}
		break;
		
		case B_REG_LAUNCH_ETC:
		{
			PRINT("B_REG_LAUNCH_ETC\n");

			_HandleLaunch( message );
		}
		break;
#endif

#if 0  // not-HoG_GENODE
		case B_REG_TEAM_DEBUGGER_ALERT:
		{
			if (fShutdownProcess != NULL)
				fShutdownProcess->PostMessage(message);
			break;
		}
#endif  // ~not-HoG_GENODE

		// roster requests
		case B_REG_ADD_APP:
			fRoster->HandleAddApplication(message);
			break;
		case B_REG_COMPLETE_REGISTRATION:
			fRoster->HandleCompleteRegistration(message);
			break;
		case B_REG_IS_APP_REGISTERED:
			fRoster->HandleIsAppRegistered(message);
			break;
		case B_REG_REMOVE_PRE_REGISTERED_APP:
			fRoster->HandleRemovePreRegApp(message);
			break;
		case B_REG_REMOVE_APP:
			fRoster->HandleRemoveApp(message);
			break;
#if 0  // not-HoG_GENODE
		case B_REG_SET_THREAD_AND_TEAM:
			fRoster->HandleSetThreadAndTeam(message);
			break;
#endif
		case B_REG_SET_SIGNATURE:
			fRoster->HandleSetSignature(message);
			break;
		case B_REG_GET_APP_INFO:
			fRoster->HandleGetAppInfo(message);
			break;
		case B_REG_GET_APP_LIST:
			fRoster->HandleGetAppList(message);
			break;
		case B_REG_UPDATE_ACTIVE_APP:
			fRoster->HandleUpdateActiveApp(message);
			break;
#if 0
		case B_REG_BROADCAST:
			fRoster->HandleBroadcast(message);
			break;
#endif
		case B_REG_START_WATCHING:
			fRoster->HandleStartWatching(message);
			break;
		case B_REG_STOP_WATCHING:
			fRoster->HandleStopWatching(message);
			break;
		case B_REG_GET_RECENT_DOCUMENTS:
			fRoster->HandleGetRecentDocuments(message);
			break;
		case B_REG_GET_RECENT_FOLDERS:
			fRoster->HandleGetRecentFolders(message);
			break;
		case B_REG_GET_RECENT_APPS:
			fRoster->HandleGetRecentApps(message);
			break;
		case B_REG_ADD_TO_RECENT_DOCUMENTS:
			fRoster->HandleAddToRecentDocuments(message);
			break;
		case B_REG_ADD_TO_RECENT_FOLDERS:
			fRoster->HandleAddToRecentFolders(message);
			break;
		case B_REG_ADD_TO_RECENT_APPS:
			fRoster->HandleAddToRecentApps(message);
			break;
		case B_REG_CLEAR_RECENT_DOCUMENTS:
			fRoster->ClearRecentDocuments();
			break;
		case B_REG_CLEAR_RECENT_FOLDERS:
			fRoster->ClearRecentFolders();
			break;
		case B_REG_CLEAR_RECENT_APPS:
			fRoster->ClearRecentApps();
			break;
#if 0
		case B_REG_LOAD_RECENT_LISTS:
			fRoster->HandleLoadRecentLists(message);
			break;
		case B_REG_SAVE_RECENT_LISTS:
			fRoster->HandleSaveRecentLists(message);
			break;

		// message runner requests
		case B_REG_REGISTER_MESSAGE_RUNNER:
			fMessageRunnerManager->HandleRegisterRunner(message);
			break;
		case B_REG_UNREGISTER_MESSAGE_RUNNER:
			fMessageRunnerManager->HandleUnregisterRunner(message);
			break;
		case B_REG_SET_MESSAGE_RUNNER_PARAMS:
			fMessageRunnerManager->HandleSetRunnerParams(message);
			break;
		case B_REG_GET_MESSAGE_RUNNER_INFO:
			fMessageRunnerManager->HandleGetRunnerInfo(message);
			break;

		// package watching requests
		case B_REG_PACKAGE_START_WATCHING:
		case B_REG_PACKAGE_STOP_WATCHING:
			fPackageWatchingManager->HandleStartStopWatching(message);
			break;
		case B_PACKAGE_UPDATE:
			fPackageWatchingManager->NotifyWatchers(message);
			break;

		// internal messages
		case B_REG_ROSTER_SANITY_EVENT:
			fRoster->CheckSanity();
			fSanityEvent->SetTime(system_time() + kRosterSanityEventInterval);
			fEventQueue->AddEvent(fSanityEvent);
			break;
		case B_REG_SHUTDOWN_FINISHED:
			if (fShutdownProcess) {
				fShutdownProcess->PostMessage(B_QUIT_REQUESTED,
					fShutdownProcess);
				fShutdownProcess = NULL;
			}
			break;

		case kMsgRestartAppServer:
		{
			fRoster->HandleRestartAppServer(message);
			break;
		}
#endif

		default:
			BApplication::MessageReceived(message);
			break;
	}
}


/*!	\brief Handle a shut down request message.
	\param request The request to be handled.
*/
void
Registrar::_HandleShutDown(BMessage *request)
{
#if 0  // not-HoG_GENODE
	status_t error = B_OK;

	// check, whether we're already shutting down
	if (fShutdownProcess)
		error = B_SHUTTING_DOWN;

	bool needsReply = true;
	if (error == B_OK) {
		// create a ShutdownProcess
		fShutdownProcess = new(nothrow) ShutdownProcess(fRoster, fEventQueue);
		if (fShutdownProcess) {
			error = fShutdownProcess->Init(request);
			if (error == B_OK) {
				DetachCurrentMessage();
				fShutdownProcess->Run();
				needsReply = false;
			} else {
				delete fShutdownProcess;
				fShutdownProcess = NULL;
			}
		} else
			error = B_NO_MEMORY;
	}

	if (needsReply)
		ShutdownProcess::SendReply(request, error);
#else
	///later: remove this temporary reboot/shutdown one-liner, enable actual "shutdown-process" code (up here) instead
	if( false == request->FindBool("confirm") )
		_kern_shutdown( request->FindBool("reboot") );
#endif
}


#if defined( HoG_GENODE )

void Registrar::_HandleSuspendToRam()
{
	_kern_suspend_to_ram();
}


void Registrar::_HandleLaunch(
	BMessage * request
	//const BStringList & environ_list
	)
{
	/*
	if( debug )
	{
		printf(" _HandleLaunch: msg:\n" );//, arg_list.Join("', '").String());
		request->PrintToStream();
	}*/
	
	// On HoG, calling load_image() would co-recursively call us, so this is
	// where we call the actual implementation, which talks to the 'Dynit' sub-init:
	//
	bool success = false;
	
	if( B_REG_LAUNCH_ETC == request->what )
	{
		BString exe_path;
		BString xml_config;
		int32 ram_quota_MB = 0;
		{
			request->FindString( "path", &exe_path );
			
			BMessage inner;
			request->FindMessage( "genode-init-params", &inner );
			inner.FindString( "Genode:InitXml:Config",	&xml_config );
			inner.FindInt32(  "Genode:InitXml:ram_MB",	&ram_quota_MB );
		}
		
		// this always lands in the "not packaged" pilot
		success = initPilot.Toggle_Xml( exe_path, xml_config, ram_quota_MB );
	}
	else  // B_REG_LAUNCH (argc, argv) :
	{
		BStringList arg_list;
		request->FindStrings( "argv", &arg_list );
		
		// route either to "packaged" or "non-packaged" init pilot:
		if( arg_list.First().FindFirst("/depot/") >= 0 && arg_list.First().EndsWith("/runtime") )
			success = deployPilot.Toggle( arg_list );
		else
			success = initPilot.Toggle( arg_list );
		
		//thread_id app_thread = Launch_or_Relaunch(...)
	}
	
	// reply to the request
//	if( app_thread >= 0 )
	if( success )
	{
		BMessage reply(B_REG_SUCCESS);
		request->SendReply(&reply);
	} else {
		BMessage reply(B_REG_ERROR);
///		reply.AddInt32("error", app_thread);
		request->SendReply(&reply);
	}
	
	///ToDo: this is where we would theoritically call resume_thread( app_thread )
}

#endif  // ~HoG_GENODE



//	#pragma mark -


/*!	\brief Creates and runs the registrar application.

	The main thread is renamed.

	\return 0.
*/
int
main()
{
	FUNCTION_START();

	// Create the global be_clipboard manually -- it will not work, since it
	// wants to talk to the registrar in its constructor, but it doesn't have
	// to and we would otherwise deadlock when initializing our GUI in the
	// app thread.
	be_clipboard = new BClipboard(NULL);

	// create and run the registrar application
	status_t error;
	Registrar *app = new Registrar(&error);
	if (error != B_OK) {
		fprintf(stderr, "REG: Failed to create the BApplication: %s\n",
			strerror(error));
		return 1;
	}

	// rename the main thread
	rename_thread(find_thread(NULL), "roster");

	PRINT("app->Run()...\n");

	try {
		app->Run();
	} catch (std::exception& exception) {
		char buffer[1024];
		snprintf(buffer, sizeof(buffer),
			"registrar main() caught exception: %s", exception.what());
		debugger(buffer);
	} catch (...) {
		debugger("registrar main() caught unknown exception");
	}

	PRINT("delete app...\n");
	delete app;

	FUNCTION_END();
	return 0;
}

