/*
 * Copyright 2015-2017 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Axel Dörfler, axeld@pinc-software.de
 */


#include <LaunchRoster.h>

#include <Application.h>
#include <String.h>
#include <StringList.h>

#ifndef HoG_GENODE
#include <launch.h>
#endif
#include <LaunchDaemonDefs.h>
#ifndef HoG_GENODE
#include <LaunchRosterPrivate.h>
#else
#	define TEST_MODE  // just to spare us the need to implement get_launch_daemon_port(), since we don't use the launch_daemon in HoG.
#endif
#include <MessengerPrivate.h>


using namespace BPrivate;


#ifndef HoG_GENODE
BLaunchRoster::Private::Private(BLaunchRoster* roster)
	:
	fRoster(roster)
{
}


BLaunchRoster::Private::Private(BLaunchRoster& roster)
	:
	fRoster(&roster)
{
}


status_t
BLaunchRoster::Private::RegisterSessionDaemon(const BMessenger& daemon)
{
	BMessage request(B_REGISTER_SESSION_DAEMON);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddMessenger("daemon", daemon);
	if (status != B_OK)
		return status;

	// send the request
	BMessage result;
	status = fRoster->fMessenger.SendMessage(&request, &result);

	// evaluate the reply
	if (status == B_OK)
		status = result.what;

	return status;
}
#endif  // ~HoG_GENODE


// #pragma mark -


BLaunchRoster::BLaunchRoster()
{
	_InitMessenger();
}


BLaunchRoster::~BLaunchRoster()
{
}


status_t
BLaunchRoster::InitCheck() const
{
	return fMessenger.Team() >= 0 ? B_OK : B_ERROR;
}


status_t
BLaunchRoster::GetData(BMessage& data)
{
	if (be_app == NULL)
		return B_BAD_VALUE;

	return GetData(be_app->Signature(), data);
}


status_t
BLaunchRoster::GetData(const char* signature, BMessage& data)
{
	if (signature == NULL || signature[0] == '\0')
		return B_BAD_VALUE;

	BMessage request(B_GET_LAUNCH_DATA);
	status_t status = request.AddString("name", signature);
	if (status == B_OK)
		status = request.AddInt32("user", getuid());
	if (status != B_OK)
		return status;

	return _SendRequest(request, data);
}


port_id
BLaunchRoster::GetPort(const char* name)
{
	if (be_app == NULL)
		return B_BAD_VALUE;

	return GetPort(be_app->Signature(), name);
}


port_id
BLaunchRoster::GetPort(const char* signature, const char* name)
{
	BMessage data;
	status_t status = GetData(signature, data);
	if (status == B_OK) {
		BString fieldName;
		if (name != NULL)
			fieldName << name << "_";
		fieldName << "port";

		port_id port = data.GetInt32(fieldName.String(), B_NAME_NOT_FOUND);
		if (port >= 0)
			return port;
	}

	return -1;
}


status_t
BLaunchRoster::Target(const char* name, const BMessage& data,
	const char* baseName)
{
	return Target(name, &data, baseName);
}


status_t
BLaunchRoster::Target(const char* name, const BMessage* data,
	const char* baseName)
{
	if (name == NULL)
		return B_BAD_VALUE;

	BMessage request(B_LAUNCH_TARGET);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("target", name);
	if (status == B_OK && data != NULL && !data->IsEmpty())
		status = request.AddMessage("data", data);
	if (status == B_OK && baseName != NULL)
		status = request.AddString("base target", baseName);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::StopTarget(const char* name, bool force)
{
	if (name == NULL)
		return B_BAD_VALUE;

	BMessage request(B_STOP_LAUNCH_TARGET);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("target", name);
	if (status == B_OK)
		status = request.AddBool("force", force);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::Start(const char* name)
{
	if (name == NULL)
		return B_BAD_VALUE;

	BMessage request(B_LAUNCH_JOB);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("name", name);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::Stop(const char* name, bool force)
{
	if (name == NULL)
		return B_BAD_VALUE;

	BMessage request(B_STOP_LAUNCH_JOB);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("name", name);
	if (status == B_OK)
		status = request.AddBool("force", force);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::SetEnabled(const char* name, bool enable)
{
	if (name == NULL)
		return B_BAD_VALUE;

	BMessage request(B_ENABLE_LAUNCH_JOB);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("name", name);
	if (status == B_OK)
		status = request.AddBool("enable", enable);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::StartSession(const char* login)
{
	if (login == NULL)
		return B_BAD_VALUE;

	BMessage request(B_LAUNCH_SESSION);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("login", login);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::RegisterEvent(const BMessenger& source, const char* name,
	uint32 flags)
{
	return _UpdateEvent(B_REGISTER_LAUNCH_EVENT, source, name, flags);
}


status_t
BLaunchRoster::UnregisterEvent(const BMessenger& source, const char* name)
{
	return _UpdateEvent(B_UNREGISTER_LAUNCH_EVENT, source, name);
}


status_t
BLaunchRoster::NotifyEvent(const BMessenger& source, const char* name)
{
	return _UpdateEvent(B_NOTIFY_LAUNCH_EVENT, source, name);
}


status_t
BLaunchRoster::ResetStickyEvent(const BMessenger& source, const char* name)
{
	return _UpdateEvent(B_RESET_STICKY_LAUNCH_EVENT, source, name);
}


status_t
BLaunchRoster::GetTargets(BStringList& targets)
{
	BMessage request(B_GET_LAUNCH_TARGETS);
	status_t status = request.AddInt32("user", getuid());
	if (status != B_OK)
		return status;

	// send the request
	BMessage result;
	status = _SendRequest(request, result);
	if (status == B_OK)
		status = result.FindStrings("target", &targets);

	return status;
}


status_t
BLaunchRoster::GetTargetInfo(const char* name, BMessage& info)
{
	return _GetInfo(B_GET_LAUNCH_TARGET_INFO, name, info);
}


status_t
BLaunchRoster::GetJobs(const char* target, BStringList& jobs)
{
	BMessage request(B_GET_LAUNCH_JOBS);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK && target != NULL)
		status = request.AddString("target", target);
	if (status != B_OK)
		return status;

	// send the request
	BMessage result;
	status = _SendRequest(request, result);
	if (status == B_OK)
		status = result.FindStrings("job", &jobs);

	return status;
}


status_t
BLaunchRoster::GetJobInfo(const char* name, BMessage& info)
{
	return _GetInfo(B_GET_LAUNCH_JOB_INFO, name, info);
}


status_t
BLaunchRoster::GetLog(BMessage& info)
{
	return _GetLog(NULL, info);
}


status_t
BLaunchRoster::GetLog(const BMessage& filter, BMessage& info)
{
	return _GetLog(&filter, info);
}


void
BLaunchRoster::_InitMessenger()
{
#ifdef TEST_MODE
	port_id daemonPort = find_port(B_LAUNCH_DAEMON_PORT_NAME);
#else
	// find the launch_daemon port
	port_id daemonPort = BPrivate::get_launch_daemon_port();
#endif
	port_info info;
	if (daemonPort >= 0 && get_port_info(daemonPort, &info) == B_OK) {
		BMessenger::Private(fMessenger).SetTo(info.team, daemonPort,
			B_PREFERRED_TOKEN);
	}
}


status_t
BLaunchRoster::_SendRequest(BMessage& request)
{
	BMessage result;
	return _SendRequest(request, result);
}


status_t
BLaunchRoster::_SendRequest(BMessage& request, BMessage& result)
{
	// Send the request, and evaluate the reply
	status_t status = fMessenger.SendMessage(&request, &result);
	if (status == B_OK)
		status = result.what;

	return status;
}


status_t
BLaunchRoster::_UpdateEvent(uint32 what, const BMessenger& source,
	const char* name, uint32 flags)
{
	if (be_app == NULL || name == NULL || name[0] == '\0')
		return B_BAD_VALUE;

	BMessage request(what);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddMessenger("source", source);
	if (status == B_OK)
		status = request.AddString("owner", be_app->Signature());
	if (status == B_OK)
		status = request.AddString("name", name);
	if (status == B_OK && flags != 0)
		status = request.AddUInt32("flags", flags);
	if (status != B_OK)
		return status;

	return _SendRequest(request);
}


status_t
BLaunchRoster::_GetInfo(uint32 what, const char* name, BMessage& info)
{
	if (name == NULL || name[0] == '\0')
		return B_BAD_VALUE;

	BMessage request(what);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK)
		status = request.AddString("name", name);
	if (status != B_OK)
		return status;

	return _SendRequest(request, info);
}


status_t
BLaunchRoster::_GetLog(const BMessage* filter, BMessage& info)
{
	BMessage request(B_GET_LAUNCH_LOG);
	status_t status = request.AddInt32("user", getuid());
	if (status == B_OK && filter != NULL)
		status = request.AddMessage("filter", filter);
	if (status != B_OK)
		return status;

	return _SendRequest(request, info);
}
