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

#undef NDEBUG
#include <assert.h>

#include <stdlib.h>
#ifndef WIN32
#include <signal.h>
#endif

#include <qapplication.h>
#include <qfont.h>
#include <qstring.h>
#include <qmessagebox.h>
#include <qtextcodec.h>
#include <qtranslator.h>
#include <qlabel.h>
#include <qstylefactory.h>
//Added by qt3to4:
#include <QCloseEvent>
#include <QNetworkProxy>

#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 <dclib/dclib.h>
#include <dclib/core/cdir.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>
#include <dclib/chttp.h>

#ifdef HAVE_SOCKS
extern "C" {
#include <socks.h>
}
#endif

#ifdef WIN32
DLL_EXPORT CTraffic CSocket::m_Traffic;
DLL_EXPORT eSocketLog CSocket::m_eSocketLog;
#endif

#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

/** */
int main(int argc, char *argv[])
{
	/* Nothing in QT works without a QApplication!!! */
	QApplication * pApplication = new QApplication(argc, argv);
	
	int i;
	int ret;
	CString configpath;
	CString debugopt;
	bool b = false;
	bool bcrash = false;
	QTranslator tor(0);
	DCSplash * pSplash = 0;
	
	// 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
	// fix start path
	CDir d;

	DCDebug::arg_0 = argv[0];

	if ( d.IsFile( argv[0], false ) )
		DCDebug::startup_dir = "./";
	else if ( d.IsFile( CString("/usr/bin/") + argv[0], false ) )
		DCDebug::startup_dir = "/usr/bin/";
	else if ( d.IsFile( CString("/usr/local/bin/") + argv[0], false ) )
		DCDebug::startup_dir = "/usr/local/bin/";
	else if ( d.IsFile( CString("/usr/X11R6/bin/") + argv[0], false ) )
		DCDebug::startup_dir = "/usr/X11R6/bin/";
	else
		printf("Can't resolve path for '%s'\n",argv[0]);
#endif

#ifdef HAVE_SOCKS
	SOCKSinit(argv[0]);
#endif
	
	printf("Valknut: '%s' ",PACKAGE_VERSION);
	printf("using dclib: '%s'\n",dclibVersion());

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

	// 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";
			s += "\n";

			printf("%s",s.toAscii().constData());
			delete pApplication;
			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 pApplication;
			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--;
			}
		}
		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];
				printf("CRASH: %s %s\n",configpath.Data(),debugopt.Data());  fflush(stdout);
				bcrash = true;
			}
			else
			{
				printf("Wrong parameter option -C\n");fflush(stdout);
				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
		{
			printf("Ignore wrong Parameter: '%s'\n",argv[i]);
		}
	}

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

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

	if ( bcrash )
	{
		printf("CRASH: %s %s\n",configpath.Data(),debugopt.Data()); fflush(stdout);
		DCDebug * dbg = new DCDebug();
		if ( dbg->Init(configpath.Data(),debugopt.Data()) )
		{
			dbg->exec();
		}
		delete dbg;
		printf("exit\n");
		kill(QString(debugopt.Data()).section(",",0,0).toUInt(), SIGCONT);
		delete pApplication;
		_exit(0);
	}

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

	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, "Valknut",
			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 ...");
	pApplication->processEvents();

	g_pConfig->LoadHubProfile();

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

	g_pConfig->LoadDCHub();

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

	CFileManager::SetInstance(new CFileManager());
	
	if (g_pConfig->GetFilelistNamingSchemeVersion() < 4)
	{
		if ( pSplash )
			pSplash->TextLabel_STATUS->setText("Renaming stored filelists ...");
		pApplication->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);
		}
		pApplication->setFont(appFont);
	}

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

	if ( !(g_pConfig->GetLanguageFile().isEmpty()) )
	{
		b = tor.load( g_pConfig->GetLanguageFile() );
	}

	if ( b )
	{
		pApplication->installTranslator( &tor );
	}

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

	DCGuiApp * dcgui = new DCGuiApp();

	assert(dcgui);

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

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Load plugins ...");
	pApplication->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 ...");
	pApplication->processEvents();

	g_pPluginManager->InitPlugins();

	if ( pSplash )
		pSplash->TextLabel_STATUS->setText("Check HTTP proxy...");
	pApplication->processEvents();
	
	char * proxyvar = getenv("http_proxy");
	{
		if ( proxyvar )
		{
			CString username;
			CString password;
			CString host;
			unsigned int port;
			
			if ( CHttp::ParseProxy(proxyvar,username,password,host,port) )
			{
				printf("Using proxy %s:%u", host.Data(), port);
				if ( username.NotEmpty() )
				{
					printf(" with %s %s", username.Data(), password.Data());
				}
				printf("\n");
				QNetworkProxy qnp;
				qnp.setType( QNetworkProxy::HttpProxy );
				if ( username.NotEmpty() )
				{
					qnp.setUser( username.Data() );
				}
				if ( password.NotEmpty() )
				{
					qnp.setPassword( password.Data() );
				}
				qnp.setHostName( host.Data() );
				qnp.setPort( port );
				QNetworkProxy::setApplicationProxy(qnp);
			}
			else
			{
				printf("Some problem with your http_proxy format.\n");
			}
		}
	}

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

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

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

	pApplication->processEvents(); // was 10

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

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

	pApplication->processEvents(); // was 10

	dcgui->initWindows();
	pApplication->processEvents(); // was 10

	// connect to hubs with the auto connect flag
	g_pConnectionManager->AutoConnect();
	pApplication->processEvents();

	ret = pApplication->exec();

	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;
#endif
	delete dcgui;

	// 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;
	g_pIconLoader = 0;

	dclibDeInitDepLibs();

	delete pApplication;

	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;
}
