/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <QtCore/QTextCodec>
#include <QtCore/QTranslator>
#include <QtGui/QApplication>

#include <time.h>
#include <errno.h>
#ifndef Q_WS_WIN
#include <sys/file.h>
#include <pwd.h>
#else
#include <winsock2.h>
#endif
#ifdef Q_WS_MAC
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "config_file.h"
#include "debug.h"
#include "emoticons.h"
#include "groups_manager.h"
#include "icons_manager.h"
#include "kadu.h"
#include "libgadu.h"
#include "message_box.h"
#include "misc.h"
#include "modules.h"
#include "protocols_manager.h"

#ifdef Q_OS_MAC
#include <Carbon/Carbon.h>

static OSStatus appleEventProcessor(const AppleEvent *ae,
				AppleEvent *event, long handlerRefCon);
#endif

class KaduApplication: public QApplication
{
	public:
		KaduApplication(int &argc, char **argv): QApplication(argc, argv)
		{
#ifdef Q_OS_MAC
			/* Install Reopen Application Event (Dock Clicked) */
			m_appleEventProcessorUPP = AEEventHandlerUPP(appleEventProcessor);
			AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
				m_appleEventProcessorUPP, (long) this, true);
#endif
		};
		void commitData(QSessionManager & manager)
		{
			kadu->quit();
		}
#ifdef Q_OS_MAC
	private:
		AEEventHandlerUPP m_appleEventProcessorUPP;
#endif
};

#ifdef Q_WS_MAC
static OSStatus appleEventProcessor(const AppleEvent *ae,
				AppleEvent *event, long handlerRefCon)
{
	KaduApplication *app = (KaduApplication *) handlerRefCon;

	OSType aeID = typeWildCard;
	OSType aeClass = typeWildCard;

	AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0,
                       &aeClass, sizeof(aeClass), 0);
	AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0,
                       &aeID, sizeof(aeID), 0);

	if (aeClass == kCoreEventClass)
	{
		if (aeID == kAEReopenApplication)
		{
			kadu->show();
		}
		return noErr;
	}

	return eventNotHandledErr;
}
#endif

void kaduQtMessageHandler(QtMsgType type, const char *msg)
{
	switch (type)
	{
		case QtDebugMsg:
			fprintf(stderr, "Debug: %s\n", msg);
			fflush(stderr);
			break;
		case QtWarningMsg:
			fprintf(stderr, "\033[34mWarning: %s\033[0m\n", msg);
			fflush(stderr);
			if (strstr(msg, "no mimesource for") == 0)
				printBacktrace("warning from Qt (above)");
			break;
		case QtFatalMsg:
			fprintf(stderr, "\033[31;1mFatal: %s\033[0m\n", msg);
			fflush(stderr);
			printBacktrace("fatal error from Qt (above)");
			abort();
		case QtCriticalMsg:
			fprintf(stderr, "\033[31;1mCritical: %s\033[0m\n", msg);
			fflush(stderr);
			printBacktrace("critical error from Qt (above)");
			abort();
	}
}

#ifdef DEBUG_ENABLED
extern KADUAPI bool showTimesInDebug;
#endif
extern KADUAPI char* SystemUserName;

void enableSignalHandling();
void disableLock();
bool isRuning(int);

int main(int argc, char *argv[])
{
	int ggnumber(0);
	time_t sec;
	int msec;
	time_t startTimeT = time(0);
	beforeExecTime = endingTime = exitingTime = 0;
	getTime(&sec, &msec);
	startTime = (sec % 1000) * 1000 + msec;

	// na Windowsie to nie ma znaczenia
#ifndef Q_WS_WIN
	char *env_lang = getenv("LANG");
	if (env_lang)
		setenv("LC_COLLATE", env_lang, true);
	else
		setenv("LC_COLLATE", "pl_PL", true);
#else
	WSADATA wsaData;

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		return 1;
	}

#endif
	debug_mask = -1;
	qInstallMsgHandler(kaduQtMessageHandler);
	xml_config_file = new XmlConfigFile();
	if (argc > 1)
	{
		ggnumber = QString(argv[1]).remove("gg:").remove("/").toInt();
		if (ggnumber < 0)
			ggnumber = 0;
	}

	config_file_ptr = new ConfigFile(ggPath(QString("kadu.conf")));
	debug_mask = config_file.readNumEntry("General", "DEBUG_MASK");
	char *d = getenv("DEBUG_MASK");
	if (d)
		debug_mask = atol(d);
	gg_debug_level = debug_mask | ~255;

	bool saveStdErr = config_file.readBoolEntry("General", "SaveStdErr");
	d = getenv("SAVE_STDERR");
	if (d)
		saveStdErr = strcmp(d, "1") == 0;
	if (saveStdErr)
	{
		char path[1024];
		struct tm *t = localtime(&startTimeT);
#ifndef Q_OS_WIN
		struct passwd *p = getpwuid(getuid());
		if (t && p)
		{
			SystemUserName=strdup(p->pw_name);
			sprintf(path, "/tmp/kadu-%s-%04d-%02d-%02d-%02d-%02d-%02d.dbg", SystemUserName, 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
			if (freopen(path, "w+", stderr) == 0)
				fprintf(stdout, "freopen: %s\n", strerror(errno));
			else if (fchmod(fileno(stderr), 0600) != 0)
			{
				fclose(stderr);
				fprintf(stdout, "can't chmod output logfile (%s)!\n", path);
			}
		}
#else
		char *tmp=getenv("TEMP");
		if(!tmp) tmp=".";
		sprintf(path, "%s\\kadu-dbg-%04d-%02d-%02d-%02d-%02d-%02d.txt", tmp, 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
		if (freopen(path, "w+", stderr) == 0)
			fprintf(stdout, "freopen: %s\n", strerror(errno));
#endif

	}

#ifdef DEBUG_ENABLED
	d = getenv("SHOW_TIMES");
	if (d)
		showTimesInDebug = atoi(d);
#endif

	enableSignalHandling();

	// delayed running, useful in gnome
#ifndef Q_OS_WIN
	sleep(config_file.readNumEntry("General", "StartDelay"));
#endif
	QString data_dir = dataPath("kadu", argv[0]);
	if (!QDir(data_dir).isReadable())
	{
		fprintf(stderr, "data directory (%s) is NOT readable, exiting...\n", qPrintable(data_dir));
		fprintf(stderr, "look at: http://www.kadu.net/msgs/data_dir_not_readable/\n");
		fflush(stderr);

		delete xml_config_file;
		delete config_file_ptr;

		exit(10);
	}

	kdebugm(KDEBUG_INFO, "before creation of new KaduApplication\n");
	new KaduApplication(argc, argv);
	kdebugm(KDEBUG_INFO, "after creation of new KaduApplication\n");

	defaultFont = new QFont(qApp->font());
	defaultFontInfo = new QFontInfo(*defaultFont);
	// loading translation
	QTranslator qt_qm(0, "Translator_qt");
	QString lang = config_file.readEntry("General", "Language", QString(QTextCodec::locale()).mid(0,2));
	qt_qm.load(dataPath(QString("kadu/translations/qt_") + lang), ".");
	qApp->installTranslator(&qt_qm);
	QTranslator kadu_qm(0, "Translator_kadu");
	kadu_qm.load(dataPath(QString("kadu/translations/kadu_") + lang), ".");
	qApp->installTranslator(&kadu_qm);

	// plugins path (win32)
	qApp->addLibraryPath(libPath("qt"));

	if(isRuning(ggnumber)){
		Kadu::setClosing();
		delete defaultFont;
		delete defaultFontInfo;
		delete config_file_ptr;
		delete xml_config_file;
//		delete qApp;
		return 1;
	}

	ProtocolsManager::initModule();
	UserList::initModule();
	IconsManager::initModule();
	GroupsManager::initModule();
	EmoticonsManager::initModule();

	new Kadu(0);

	qApp->setMainWidget(kadu);
	QPixmap pix;
#ifdef Q_OS_MACX
	pix = icons_manager->loadPixmap("BigOffline");
#else
	pix = icons_manager->loadPixmap("Offline");
#endif
	kadu->setMainWindowIcon(pix);

	QString path_ = ggPath(QString::null);
#ifndef Q_OS_WIN
	if (path_.endsWith("/kadu/") || path_.endsWith("/Kadu/")) // for profiles directory
		mkdir(qPrintable(path_.left(path_.length() - 6)), 0700);
	mkdir(qPrintable(path_), 0700);
#else
	QDir().mkdir(path_);
#endif

	ModulesManager::initModule();

	kadu->startupProcedure();

	QObject::connect(qApp, SIGNAL(aboutToQuit()), kadu, SLOT(quitApplication()));

	// if someone is running Kadu from root account, let's remind him
	// that it's a "bad thing"(tm) ;) (usually for win32 users)
	// and disable this feature for win32 ;)
#ifndef Q_OS_WIN
	if (geteuid() == 0)
		MessageBox::msg(qApp->translate("@default", QT_TR_NOOP("Please do not run Kadu as a root!\nIt's a high security risk!")), false, "Warning");
#endif
	QTimer::singleShot(15000, kadu, SLOT(deleteOldConfigFiles()));
	if (ggnumber)
		qApp->postEvent(kadu, new OpenGGChatEvent(ggnumber));

	/* for testing of startup / close time */
	char *close_after = getenv("CLOSE_AFTER");
	if (close_after)
	{
		int tm = atoi(close_after);
		if (tm >= 0)
			QTimer::singleShot(tm, kadu, SLOT(quit()));
	}

	/* for testing of startup / close time */
	measureTime = getenv("MEASURE_TIME") != 0;

	if (measureTime)
	{
		getTime(&sec, &msec);
		beforeExecTime = (sec % 1000) * 1000 + msec;
	}

	int ret = qApp->exec();
	kdebugm(KDEBUG_INFO, "after exec\n");

	// clear lockfile
	disableLock();

	qApp->removeTranslator(&qt_qm);
	qApp->removeTranslator(&kadu_qm);
#ifdef Q_WS_WIN
	WSACleanup();
#endif

	if (measureTime)
	{
		getTime(&sec, &msec);
		exitingTime = (sec % 1000) * 1000 + msec;
		fprintf(stderr, "init time:%lld, run time:%lld, ending time:%lld\n", beforeExecTime - startTime, endingTime - beforeExecTime, exitingTime - endingTime);
	}

//	delete qApp; //sometimes leads to segfault
	qApp->deleteLater();
//	qApp = 0;
	kdebugm(KDEBUG_INFO, "exiting main\n");
	return ret;
}
