/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2002 Chris Schoeneman
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file COPYING that should have accompanied this file.
 * 
 * This package 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.
 */

#include "CLog.h"
#include "ILogOutputter.h"
#include "CArch.h"
#include "CStringUtil.h"
#include "XArch.h"
#include "CAutoStart.h"
#include "LaunchUtil.h"
#include "resource.h"

#define CLIENT_DAEMON_NAME "Synergy Client"
#define SERVER_DAEMON_NAME "Synergy Server"
#define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others."
#define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others."

//
// CAutoStartOutputter
//
// This class detects a message above a certain level and saves it
//

class CAutoStartOutputter : public ILogOutputter {
public:
	CAutoStartOutputter(CString* msg) : m_msg(msg) { }
	virtual ~CAutoStartOutputter() { }

	// ILogOutputter overrides
	virtual void		open(const char*) { }
	virtual void		close() { }
	virtual bool		write(ELevel level, const char* message);
	virtual const char*	getNewline() const { return ""; }

private:
	CString*			m_msg;
};

bool
CAutoStartOutputter::write(ELevel level, const char* message)
{
	if (level <= CLog::kERROR) {
		*m_msg = message;
	}
	return false;
}


//
// CAutoStart
//

CAutoStart*				CAutoStart::s_singleton = NULL;

CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) :
	m_parent(parent),
	m_config(config),
	m_isServer(config != NULL),
	m_cmdLine(cmdLine),
	m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME),
	m_userConfigSaved(false)

{
	assert(s_singleton == NULL);
	s_singleton = this;
}

CAutoStart::~CAutoStart()
{
	s_singleton = NULL;
}

void
CAutoStart::doModal()
{
	// install our log outputter
	CLOG->insert(new CAutoStartOutputter(&m_errorMessage));

	// reset saved flag
	m_userConfigSaved = false;

	// do dialog
	DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART),
								m_parent, dlgProc, (LPARAM)this);

	// remove log outputter
	CLOG->pop_front();
}

bool
CAutoStart::wasUserConfigSaved() const
{
	return m_userConfigSaved;
}

void
CAutoStart::update()
{
	// get installation state
	const bool installedSystem = ARCH->isDaemonInstalled(
										m_name.c_str(), true);
	const bool installedUser   = ARCH->isDaemonInstalled(
										m_name.c_str(), false);

	// get user's permissions
	const bool canInstallSystem = ARCH->canInstallDaemon(
										m_name.c_str(), true);
	const bool canInstallUser   = ARCH->canInstallDaemon(
										m_name.c_str(), false);

	// update messages
	CString msg, label;
	if (canInstallSystem) {
		msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM);
	}
	else if (canInstallUser) {
		msg = getString(IDS_AUTOSTART_PERMISSION_USER);
	}
	else {
		msg = getString(IDS_AUTOSTART_PERMISSION_NONE);
	}
	setWindowText(getItem(m_hwnd, IDC_AUTOSTART_PERMISSION_MSG), msg);
	if (installedSystem) {
		msg   = getString(IDS_AUTOSTART_INSTALLED_SYSTEM);
		label = getString(IDS_UNINSTALL_LABEL);
	}
	else if (installedUser) {
		msg = getString(IDS_AUTOSTART_INSTALLED_USER);
		label = getString(IDS_UNINSTALL_LABEL);
	}
	else {
		msg = getString(IDS_AUTOSTART_INSTALLED_NONE);
		label = getString(IDS_INSTALL_LABEL);
	}
	setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALLED_MSG), msg);

	// update buttons
	setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM), label);
	setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER), label);
	if (installedSystem) {
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem);
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, false);
		m_install = false;
	}
	else if (installedUser) {
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, false);
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser);
		m_install = false;
	}
	else {
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem);
		enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser);
		m_install = true;
	}
}

bool
CAutoStart::onInstall(bool allUsers)
{
	if (!m_install) {
		return onUninstall(allUsers);
	}

	// try saving configuration.  if we can't then don't try
	// installing the daemon.
	if (m_config != NULL) {
		if (!saveConfig(*m_config, allUsers)) {
			showError(m_hwnd, CStringUtil::format(
								getString(IDS_SAVE_FAILED).c_str(),
								getErrorString(GetLastError()).c_str()));
			return false;
		}

		// note if we've saved the user's configuration
		if (!allUsers) {
			m_userConfigSaved = true;
		}
	}

	// get the app path
	CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP);

	// clear error message
	m_errorMessage = "";

	// install
	try {
		ARCH->installDaemon(m_name.c_str(),
					m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO,
					appPath.c_str(), m_cmdLine.c_str(), allUsers);
		askOkay(m_hwnd, getString(IDS_INSTALL_TITLE),
								getString(allUsers ?
									IDS_INSTALLED_SYSTEM :
									IDS_INSTALLED_USER));
		return true;
	}
	catch (XArchDaemon& e) {
		if (m_errorMessage.empty()) {
			m_errorMessage = CStringUtil::format(
								getString(IDS_INSTALL_GENERIC_ERROR).c_str(),
								e.what().c_str());
		}
		showError(m_hwnd, m_errorMessage);
		return false;
	}
}

bool
CAutoStart::onUninstall(bool allUsers)
{
	// clear error message
	m_errorMessage = "";

	// uninstall
	try {
		ARCH->uninstallDaemon(m_name.c_str(), allUsers);
		askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE),
								getString(allUsers ?
									IDS_UNINSTALLED_SYSTEM :
									IDS_UNINSTALLED_USER));
		return true;
	}
	catch (XArchDaemon& e) {
		if (m_errorMessage.empty()) {
			m_errorMessage = CStringUtil::format(
								getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(),
								e.what().c_str());
		}
		showError(m_hwnd, m_errorMessage);
		return false;
	}
}

BOOL
CAutoStart::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
{
	switch (message) {
	case WM_INITDIALOG:
		// save our hwnd
		m_hwnd = hwnd;

		// update the controls
		update();

		return TRUE;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_AUTOSTART_INSTALL_SYSTEM:
			onInstall(true);
			update();
			return TRUE;

		case IDC_AUTOSTART_INSTALL_USER:
			onInstall(false);
			update();
			return TRUE;

		case IDCANCEL:
			EndDialog(hwnd, 0);
			m_hwnd = NULL;
			return TRUE;
		}
		break;

	default:
		break;
	}

	return FALSE;
}

BOOL CALLBACK
CAutoStart::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
}