/***************************************************************************
                          main.cpp  -  description
                             -------------------
    begin                : Mon Okt  1 19:59:45 CEST 2001
    copyright            : (C) 2001-2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 <dclib/dcos.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#ifndef WIN32
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include <QApplication>
#include <QtGui>
#include <QFont>
#include <QString>
#include <QMessageBox>
#include <QTextCodec>
#include <QTranslator>
#include <QLabel>
#include <QStyleFactory>
#include <QFile>
#include <QDir>

#include "dcconfig.h"
#include "dcsplash.h"
#include "dcgui.h"
#include "dcpluginmanager.h"
#include "dcconnectionmanager.h"
#include "dctransferview.h"
#include "dcdebug.h"
#include "dciconloader.h"
#include "dcsigterm.h"
#include "dchubsearch.h"
#include "dchublinklistener.h"

#include <dclib/dclib.h>
#include <dclib/core/casyncdns.h>
#include <dclib/core/cmanager.h>
#include <dclib/cquerymanager.h>
#include <dclib/cfilemanager.h>
#include <dclib/clistenmanager.h>
#include <dclib/csearchmanager.h>

#ifndef WIN32
/** install signal handler */
void term_install_handlers(void)
{
	// ignore pipe signal
	// signal(SIGPIPE, SIG_IGN);
	if ( can_we_handle_signal(SIGPIPE) )
	{
		struct sigaction new_action;
		new_action.sa_handler = SIG_IGN;
		sigemptyset( &new_action.sa_mask );
		new_action.sa_flags = 0;
		if ( sigaction(SIGPIPE,&new_action,NULL) == -1 )
		{
			printf("Error ignoring SIGPIPE.\n");
		}
	}

	// install SIGTERM signal handler
	// signal(SIGTERM, sigterm_handler);

	// install handlers for SIGTERM, SIGHUP, SIGINT and SIGQUIT
	bool term = false, hup = false, sigint = false, quit = false;
	
	struct sigaction new_action;
	new_action.sa_handler = DCSigTerm::signal_handler;
	sigemptyset( &new_action.sa_mask );
	new_action.sa_flags = 0;
	
	term = can_we_handle_signal( SIGTERM );
	if ( term )
	{
		sigaddset( &new_action.sa_mask, SIGTERM );
	}
	
	hup = can_we_handle_signal( SIGHUP );
	if ( hup )
	{
		sigaddset( &new_action.sa_mask, SIGHUP );
	}
	
	sigint = can_we_handle_signal( SIGINT );
	if ( sigint )
	{
		sigaddset( &new_action.sa_mask, SIGINT );
	}
	
	quit = can_we_handle_signal( SIGQUIT );
	if ( quit )
	{
		sigaddset( &new_action.sa_mask, SIGQUIT );
	}
	
	if ( term )
	{
		if ( sigaction(SIGTERM,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGTERM handler\n");
		}
	}
	
	if ( hup )
	{
		if ( sigaction(SIGHUP,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGHUP handler\n");
		}
	}
	
	if ( sigint )
	{
		if ( sigaction(SIGINT,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGINT handler\n");
		}
	}
	
	if ( quit )
	{
		if ( sigaction(SIGQUIT,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGQUIT handler\n");
		}
	}
}
#endif

#ifndef WIN32
int fd_lock;

int get_lock(){
    struct flock fl;

    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start = 0;
    fl.l_len = 0;

    QString lock_file = QDir::homePath()+"/.eiskaltdc/eiskaltdc.lock";

    if ((fd_lock = open(lock_file.toAscii().data(), O_WRONLY|O_CREAT, 0666)) < 0)
    {
        perror("open");
        exit(1);
    }

    if (fcntl(fd_lock, F_SETLK, &fl) < 0){
        perror("fcntl flock");
        return 0;
    }

    return 1;
}

#endif

/** */
int main(int argc, char *argv[])
{
	/* Nothing in QT works without a QApplication!!! */
	new QApplication(argc, argv);
	
	int i;
	int ret;
	CString configpath;
	CString debugopt;
        CString magnet;
	CString dchublink;
	bool bcrash = false;
	QTranslator tor(0);
	DCSplash * pSplash = 0;

        // translating Qt UI Interface
        QTranslator qtTranslator;
        qtTranslator.load("qt_" + QLocale::system().name(),
        QLibraryInfo::location( QLibraryInfo::TranslationsPath) );
        qApp->installTranslator (&qtTranslator );
        // end of translating

	// startup parameter
	bool bThemeSupport = true;
	bool bSplash = true;
	bool bTray = false;
	bool bOverrideTray = false;
	bool bEnableTray = true;

#ifdef WIN32
#ifdef _DEBUG
	//_CrtSetDbgFlag ( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF );
#endif
#endif
	bool bBacktrace = true;

#ifndef WIN32
	DCDebug::arg_0 = qApp->applicationFilePath().toLocal8Bit().constData();
	DCDebug::startup_dir = qApp->applicationDirPath().toLocal8Bit().constData();
#endif
	
	printf("EiskaltDC: '%s' ",PACKAGE_VERSION);
	printf("using dclib: '%s'\n",dclibVersion());

	// check for Qt (TM) version
	printf("Checking Qt® version... ");
	printf("compiled for '%s' using '%s'\n",QT_VERSION_STR,qVersion());

	// makes QString::toAscii().constData() and fromAscii() return the correct local encoding not just ISO-8859-1
	QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
	// QT4 just says "System" which is somewhat unhelpful
	//printf("QT thinks your system encoding is '%s'\n",QTextCodec::codecForLocale()->name().data());
	//printf("If this is incorrect, you may need to adjust your LANG environment variable which is currently '%s'\n",CString(getenv("LANG")).Data());

	// parameter stuff ...
	for(i=1;i<argc;i++)
	{
		if ( QString(argv[i]) == "--help" )
		{
			QString s ;

			s  = "\nHelp:\n\n";
			s += "--help               - show this help\n";
			s += "--disable-splash     - disable splash screen\n";
			s += "--disable-theme      - disable theme support\n";
			s += "--enable-tray        - enable tray icon, overriding saved setting\n";
			s += "--disable-tray       - disable tray icon, overriding saved setting\n";
			s += "--check-theme        - check for broken theme support\n";
			s += "--disable-backtrace  - no backtrace\n";
			s += "-c <configpath>      - set another config path\n";
			s += "-v                   - verbose \n";
			s += "-vv                  - more verbose \n";
			s += "--trayicon           - start in tray icon mode\n";
			s += "--socketlog <param>  - log socket to dcsocket.log file (param = [none,send,recv,both])\n";
#ifndef WIN32
                        s += "-m <magnet:?xt=link> - auto search file by magnet-link\n";
                        s += "<magnet:?xt=link>    - auto search file by magnet-link\n";
                        s += "<dchub://host:port>  - connect with host:port\n";
#endif
			s += "\n";

			printf("%s",s.toAscii().constData());
			delete qApp;
			return 0;
		}
		else if ( QString(argv[i]) == "--disable-splash" )
			bSplash = false;
		else if ( QString(argv[i]) == "--disable-theme" )
			bThemeSupport = false;
		else if ( QString(argv[i]) == "--enable-tray" )
		{
			printf("The system tray icon is now controlled within valknut.\nUsing --enable-tray overrides the saved setting.\n");
			bOverrideTray = true;
			bEnableTray = true;
		}
		else if ( QString(argv[i]) == "--disable-tray" )
		{
			printf("The system tray icon is now controlled within valknut.\nUsing --disable-tray overrides the saved setting.\n");
			bOverrideTray = true;
			bEnableTray = false;
		}
		else if ( QString(argv[i]) == "--check-theme" )
		{
			// check style/theme Qt (TM) support
			printf("Check theme support ... ");
			QStringList list = QStyleFactory::keys();
			printf("ok %d styles found\n",list.count());
			delete qApp;
			return 0;
		}
		else if ( QString(argv[i]) == "--disable-backtrace" )
		{
			bBacktrace = false;
		}
		else if ( QString(argv[i]) == "-c" )
		{
			i++;
			if ( i<argc )
			{
				configpath = argv[i];
			}
			else
			{
				printf("Wrong parameter option -c\n");
				i--;
			}
		}
#ifndef WIN32
                else if ( QString(argv[i]) == "-m" )
                {
                        i++;
                        if ( i<argc )
                        {
                                magnet = argv[i];
                        }
                        else
                        {
                                printf("Wrong parameter option -m\n");
                                i--;
                        }
                }
#endif
		else if ( QString(argv[i]) == "--trayicon" )
		{
			bTray = true;
		}
		else if ( QString(argv[i]) == "-C" )
		{
			i++;
			if ( i<argc )
			{
				// crash handling
				printf("handle crash\n");
				debugopt = argv[i];
				bcrash = true;
			}
			else
			{
				printf("Wrong parameter option -C\n");
				i--;
			}
		}
		else if ( QString(argv[i]) == "-v" )
		{
			setdclibVerbose(1);
		}
		else if ( QString(argv[i]) == "-vv" )
		{
			setdclibVerbose(2);
		}
		else if ( QString(argv[i]) == "--socketlog" )
		{
			i++;
			if ( i<argc )
			{
				if ( QString(argv[i]) == "none" )
					CSocket::m_eSocketLog = eslNONE;
				else if ( QString(argv[i]) == "send" )
					CSocket::m_eSocketLog = eslSEND;
				else if ( QString(argv[i]) == "recv" )
					CSocket::m_eSocketLog = eslRECV;
				else if ( QString(argv[i]) == "both" )
					CSocket::m_eSocketLog = eslBOTH;
				else
				{
					printf("Wrong parameter option --socketlog!\n");
					i--;
				}
			}
			else
			{
				printf("Wrong parameter option --socketlog\n");
				i--;
			}
		}
		else
		{
			QString some_arg(argv[i]);

			some_arg = some_arg.trimmed();

			if (some_arg.indexOf("dchub://") == 0){
			    dchublink = argv[i];
			}
			else if (some_arg.indexOf("magnet:?xt=") == 0){
			    magnet = argv[i];
			}
			else
			    printf("Ignore wrong Parameter: '%s'\n",argv[i]);
		}
	}

	QString homepath = QDir::homePath();
	QString eis_path = homepath + "/.eiskaltdc";
	QDir eis_dir(eis_path);

	if (!eis_dir.exists())
	{
	    if (mkdir(eis_path.toAscii().data(), 0744) == -1)
	    {
		perror("mkdir");

		exit(1);
	    }
	}

#ifndef WIN32

        if(magnet.Length() > 0) {
		QFile *file = new QFile(QString(eis_path+"/eiskaltdc.magnet"));
                file->open(QIODevice::WriteOnly);
                char *block = magnet.Data();
                printf("Found magnet: %s\n", block);
                file->writeBlock(block, strlen(block));
                file->close();
                delete file;
        }

	if(dchublink.Length() > 0) {
		QFile *file = new QFile(QString(eis_path+"/eiskaltdc.hublink"));
		file->open(QIODevice::WriteOnly);
		char *block = dchublink.Data();
		printf("Found hublink: %s\n", block);
		file->writeBlock(block, strlen(block));
		file->close();
		delete file;
	}

        if (!get_lock()){
            printf("Process already running! Terminating.\n");
            exit(0);
        }

#endif

	// fix wrong parameter
	if ( bTray && (bOverrideTray && (bEnableTray == false)) )
	{
		bTray = false;
	}
	
	dclibInitDepLibs( argv[0] );
	
	ret = 0;

	CManager::SetInstance(new CManager);
	CAsyncDns::SetInstance(new CAsyncDns);

	if ( bcrash )
	{
		printf("CRASH: %s %s\n",configpath.Data(),debugopt.Data());
		DCDebug * dbg = new DCDebug();
		if ( dbg->Init(configpath.Data(),debugopt.Data()) )
		{
			dbg->exec();
		}
		delete dbg;
		printf("exit\n");
		delete qApp;
		_exit(0);
	}

#ifndef WIN32
	if(bBacktrace)
		crash_install_handlers();
#endif
	new DCIconLoader();

        configpath = eis_path.toAscii().data();

	CConfig::SetInstance(new DCConfig(configpath));

	if ( g_pConfig->LoadDCLib() != 0 )
	{
		ret = -1;
	}

	// set theme support
	g_pConfig->SetThemeSupport(bThemeSupport);

	if ( g_pConfig->LoadDCGui() != 0 )
	{
		ret = -1;
	}
	
	// needs to be done after config loading for icon themes
	if ( g_pIconLoader->Load() )
	{
		QMessageBox::critical( 0, "EiskaltDC",
			QString("Can't load all Icons. Please set correct Datapath and restart the Application."));
	}
	
	if ( bOverrideTray )
	{
		g_pConfig->SetEnableTray(bEnableTray);
	}
	
	if ( bSplash )
	{
		pSplash = new DCSplash(0);
		pSplash->show();
	}
	
	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Load Hub-Profiles ...");
	qApp->processEvents();

	g_pConfig->LoadHubProfile();

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Load Hub-List ...");
	qApp->processEvents();

	g_pConfig->LoadDCHub();

	// create the filemanager
	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Init Filemanager ...");
	qApp->processEvents(); // was 1

	CFileManager::SetInstance(new CFileManager());
	
	if (g_pConfig->GetFilelistNamingSchemeVersion() < 4)
	{
		if ( pSplash )
			pSplash->TextLabel_STATUS->setText("Renaming stored filelists ...");
		qApp->processEvents();
		
		g_pConfig->RenameStoredFilelists();
	}
	
	// create the querymanager
	CQueryManager::SetInstance(new CQueryManager());
	// create listenmanager
	CListenManager::SetInstance(new CListenManager());
	
	if ( dclibSupportsSSL() )
	{
		CCryptoListenManager::SetInstance( new CCryptoListenManager());
	}

	// Create search manager which is no longer part of the search window
	// in preparation for more than one search window.
	// Strangely it was being created after all the other
	// dclib managers but being deleted after CFileManager
	// CQueryManager and CAsynDns were deleted.
	CSearchManager::SetInstance(new CSearchManager());

	// set application font
	if ( g_pConfig->GetUseCustomFont() )
	{
		QFont appFont;
		if (appFont.fromString( g_pConfig->GetAppFont() ) == false)
		{
			// In case the xml file is messed up
			appFont.fromString(DEFAULT_APP_FONT);
		}
		qApp->setFont(appFont);
	}

	// load translation files
	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Install translator ...");
	qApp->processEvents();

        if ( !(g_pConfig->GetLanguageFile().isEmpty()) ){
            if ( tor.load( g_pConfig->GetLanguageFile() ) )
			qApp->installTranslator( &tor );
        }
        else {

            QString tr_file = QDir::toNativeSeparators(g_pConfig->GetValknutDataPath());
            QString lc_prefix = QLocale::system().name();

            lc_prefix = lc_prefix.left(lc_prefix.indexOf("_"));

            tr_file += "/translation/eiskaltdc." + lc_prefix + ".qm";

            if (QFile::exists(tr_file)){
                if (tor.load(tr_file)){
                    qApp->installTranslator( &tor );

                    g_pConfig->SetLanguageFile(tr_file);
                }
            }
        }

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Start ...");
	qApp->processEvents();

	new DCGuiApp();

#ifndef WIN32
	DCSigTerm * sighandler = new DCSigTerm( g_pMainWin );
	term_install_handlers();
#endif

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Load plugins ...");
	qApp->processEvents();

	CPluginManager::SetInstance(new DCPluginManager());
	// load dclib plugins
	g_pPluginManager->Load();
	// load valknut plugins
	/* 1. There are no valknut plugins.
	 * 2. /usr/share/valknut is an architecture independent folder
	 *    so is the wrong place to look for plugins
	if ( !(g_pConfig->GetValknutDataPath().isEmpty()) )
		g_pPluginManager->Load( (g_pConfig->GetValknutDataPath()+DIRSEPARATOR+"plugin").toAscii().constData());
	*/

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Init plugins ...");
	qApp->processEvents();

	g_pPluginManager->InitPlugins();

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Start ...");
	qApp->processEvents();

	// hide splash
	if ( pSplash )
		pSplash->hide();
	qApp->processEvents(); // was 10
	if ( pSplash )
		delete pSplash;

	if ( ret != 0 )
	{
		// open options dialog ...
		g_pMainWin->ShowOptionsDialog();
	}

	qApp->processEvents(); // was 10

	// load download manager queue
	g_pTransferView->DLM_LoadQueue();
	qApp->processEvents(); // was 10

	if ( bTray && g_pConfig->GetEnableTray() )
	{
		g_pMainWin->Dock();
	}
	else
	{
		StringMap * map;
                if ( g_pConfig->GetMap("MAINVIEW",map) && ((*map)["MAXIMIZED"].toInt() == 1) && !g_pConfig->GetInTray() )
		{
			g_pMainWin->showMaximized();
		}
                else if (!g_pConfig->GetInTray())
                {
                        g_pMainWin->show();
                }
		else
		{
                        g_pMainWin->hide();
		}
	}

	qApp->processEvents(); // was 10

	g_pMainWin->initWindows();
	qApp->processEvents(); // was 10

	// connect to hubs with the auto connect flag
	g_pConnectionManager->AutoConnect();
	qApp->processEvents();
#ifndef WIN32

        new DCWaitMagnet();

	new DCHubLinkListener();

#endif
	ret = qApp->exec();

        printf("Exiting...\n");

	g_pPluginManager->DeInitPlugins();

	delete g_pPluginManager;

	if ( CFileManager::Instance() )
	{
		delete CFileManager::Instance();
	}

	if ( CQueryManager::Instance() )
	{
		delete CQueryManager::Instance();
	}

	if ( CAsyncDns::Instance() )
	{
		delete CAsyncDns::Instance();
	}

#ifndef WIN32
	delete sighandler;
        delete g_pWaitMagnet;
	delete g_pHubLinkListener;
#endif
	delete g_pMainWin;

	// must be after dcgui deleted
	if ( CSearchManager::Instance() )
	{
		delete CSearchManager::Instance();
	}

	// stop listen manager before deleting g_pConfig
	// avoids possible segfault on exit
	if ( CListenManager::Instance() )
	{
		delete CListenManager::Instance();
	}

	if ( CCryptoListenManager::Instance() )
	{
		delete CCryptoListenManager::Instance();
	}

	if( g_pConfig )
	{
		// save traffic
		g_pConfig->SaveDCLib();

		delete g_pConfig;
	}

	if ( CManager::Instance() )
	{
		delete CManager::Instance();
	}

	delete g_pIconLoader;

	dclibDeInitDepLibs();

	delete qApp;

	printf("application exit ok %d\n",ret);

//	if(bBacktrace)
//		signal(SIGSEGV, 0);

#ifndef WIN32
	//signal(SIGTERM, 0);
#endif

#ifdef WIN32
#ifdef _DEBUG
	_CrtDumpMemoryLeaks();
#endif
#endif

	return ret;
}
