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

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

DCGuiApp * dcgui = 0;

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

#ifndef WIN32
/** signal handler */
void signal_handler( int signum )
{
	printf("handle ");
	print_signal_name(signum);
	printf(" ...\n");
	
	QApplication::postEvent( dcgui, new QCloseEvent() );
}

/** install signal handler */
void term_install_handlers(void)
{
	// ignore pipe signal
	// signal(SIGPIPE, SIG_IGN);
	if ( can_we_handle_signal(SIGPIPE) == TRUE )
	{
		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 = signal_handler;
	sigemptyset( &new_action.sa_mask );
	new_action.sa_flags = 0;
	
	term = can_we_handle_signal( SIGTERM );
	if ( term == TRUE )
	{
		sigaddset( &new_action.sa_mask, SIGTERM );
	}
	
	hup = can_we_handle_signal( SIGHUP );
	if ( hup == TRUE )
	{
		sigaddset( &new_action.sa_mask, SIGHUP );
	}
	
	sigint = can_we_handle_signal( SIGINT );
	if ( sigint == TRUE )
	{
		sigaddset( &new_action.sa_mask, SIGINT );
	}
	
	quit = can_we_handle_signal( SIGQUIT );
	if ( quit == TRUE )
	{
		sigaddset( &new_action.sa_mask, SIGQUIT );
	}
	
	if ( term == TRUE )
	{
		if ( sigaction(SIGTERM,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGTERM handler\n");
		}
	}
	
	if ( hup == TRUE )
	{
		if ( sigaction(SIGHUP,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGHUP handler\n");
		}
	}
	
	if ( sigint == TRUE )
	{
		if ( sigaction(SIGINT,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGINT handler\n");
		}
	}
	
	if ( quit == TRUE )
	{
		if ( sigaction(SIGQUIT,&new_action,NULL) == -1 )
		{
			printf("Error installing SIGQUIT handler\n");
		}
	}
}
#endif

/** */
int main(int argc, char *argv[])
{
	int i;
	int ret;
	CString configpath = "";
	CString debugopt = "";
	QApplication * pApplication;
	bool b = FALSE;
	bool bcrash = FALSE;
	QTranslator tor(0);
	DCSplash * pSplash = 0;

	// startup parameter
	bool bThemeSupport  = TRUE;
	bool bSplash = TRUE;
	bool bTray = 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
	
	Q_INIT_RESOURCE(images);
	
	// check xml version
	LIBXML_TEST_VERSION;

	// check xml
	printf("Checking xml version ...\n");
	printf("Compiled for '%s' using '%s'\n",LIBXML_VERSION_STRING,xmlParserVersion);

	printf("Using Valknut: '%s'\n",VERSION);
	// check dclib version
	printf("Checking dclib version ...\n");
	printf("Using DCLib: '%s'\n",dclibVersion());

	// check for Qt (TM) version and QSharedDoubleBuffer
	printf("Checking Qt (TM) version ...\n");
	printf("Compiled for '%s' using '%s'\n",QT_VERSION_STR,qVersion());
	fflush(stdout);
	/* printf("Checking Qt (TM) privates ...\n");fflush(stdout);
	QSharedDoubleBuffer * buffer = new QSharedDoubleBuffer();
	delete buffer;
	printf("private test ok !\n");fflush(stdout); */

	// makes QString::toAscii().data() 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());


#ifndef WIN32
	term_install_handlers();
#endif

	// 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 += "--disable-tray      - disable tray icon\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().data());
			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]) == "--disable-tray" )
			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());
			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 == TRUE) && (bEnableTray == FALSE) )
	{
		bTray = FALSE;
	}
	
	pApplication = new QApplication(argc, argv);

	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;
		if ( dbg.Init(configpath.Data(),debugopt.Data()) == TRUE )
		{
			dbg.exec();
		}
		printf("exit\n");
		kill(QString(debugopt.Data()).section(",",0,0).toUInt(), SIGCONT);
		_exit(0);
	}

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

	assert(new DCConfig(configpath));

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

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

	if ( g_pConfig->LoadDCGui() != 0 )
	{
		ret = -1;
	}
	
	// unset codec if necessary
	if ( g_pConfig->GetRemoteEncoding().ToLower() == "none" )
	{
		QTextCodec::setCodecForCStrings( 0 );
	}
	
	// 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."));
	}
	
	// load user icons moved out of LoadDCGui()
	g_pConfig->SetUserIcons(g_pIconLoader->GetPixmap(eiUSERICONS));
	
	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();

	if (g_pConfig->GetFilelistNamingSchemeVersion() < 2)
	{
		if ( pSplash )
			pSplash->TextLabel_STATUS->setText("Renaming stored filelists ...");
		pApplication->processEvents();
		
		g_pConfig->RenameStoredFilelists();
	}

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

	CFileManager::SetInstance(new CFileManager());
	
	// force filelist refresh on upgrade to 0.3.14
	if (g_pConfig->GetFilelistNamingSchemeVersion() < 3)
	{
		CList<DCConfigShareFolder> * list = new CList<DCConfigShareFolder>;
		
		// only needed if refresh on startup is off and some folders are shared
		if ( (g_pConfig->GetAutoRecreateShareList() == FALSE) && (g_pConfig->GetSharedFolders(list) > 0) )
		{
			printf("Refreshing list due to 0.3.14 upgrade, this should only happen once.\n");
			CFileManager::Instance()->CreateShareList();
		}
		
		delete list;
		
		g_pConfig->SetFilelistNamingSchemeVersion(3);
	}
	
	// create the querymanager
	CQueryManager::SetInstance(new CQueryManager());
	// create listenmanager
	CListenManager::SetInstance(new CListenManager());

	// set application font
	if ( g_pConfig->GetUseCustomFont() == TRUE )
	{
		QFont appFont;
		if (appFont.fromString( QString(g_pConfig->GetAppFont().Data()) ) == 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() != "" )
	{
		b = tor.load( g_pConfig->GetLanguageFile().Data(), "." );
	}

	if ( !b )
	{
		b = tor.load( QString("valknut.") + QTextCodec::locale(), "." );
	}

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

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

	dcgui = new DCGuiApp();

	assert(dcgui);

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

	assert(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() != "" )
		g_pPluginManager->Load( (g_pConfig->GetValknutDataPath()+DIRSEPARATOR+"plugin").toAscii().data());
	*/

	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) == TRUE )
			{
				printf("Using proxy %s:%u", host.Data(), port);
				if ( username != "" )
				{
					printf(" with %s %s", username.Data(), password.Data());
				}
				printf("\n");
				QNetworkProxy qnp;
				qnp.setType( QNetworkProxy::HttpProxy );
				if ( username != "" )
				{
					qnp.setUser( username.Data() );
				}
				if ( password != "" )
				{
					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->setMainWidget(dcgui);
	pApplication->processEvents(); // was 10

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

	if ( bTray == TRUE )
	{
		dcgui->hide();
		dcgui->Dock();
	}
	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();
	}

	pApplication->setMainWidget(0);
	delete dcgui;
	delete pApplication;

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

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

		delete g_pConfig;
	}

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

	delete g_pIconLoader;
	g_pIconLoader = 0;

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