//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//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.
//
//This program 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.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifdef _DEBUG
	#define _CRTDBG_MAP_ALLOC
	#include <stdlib.h>
	#include <crtdbg.h>
#endif

#include "stdafx.h"
#include "emule.h"
#include "emuleDlg.h"
#include "opcodes.h"
#include "mdump.h"
#include "Webserver.h"
#include <..\src\mfc\sockimpl.h>
#include <..\src\mfc\afximpl.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW

static CMemoryState oldMemState, newMemState, diffMemState;
//CString _strCrtDebugReportFilePath(_T("eMule CRT Debug Log.txt"));
// don't use a CString for that memory - it will not be available on application termination!
#define APP_CRT_DEBUG_LOG_FILE _T("eMule CRT Debug Log.txt")
static TCHAR _szCrtDebugReportFilePath[MAX_PATH] = APP_CRT_DEBUG_LOG_FILE;
#endif


const static UINT UWM_ARE_YOU_EMULE=RegisterWindowMessage(_T(EMULE_GUID));

// CemuleApp

BEGIN_MESSAGE_MAP(CemuleApp, CWinApp)
	ON_COMMAND(ID_HELP, OnHelp)
END_MESSAGE_MAP()


CemuleApp::CemuleApp(LPCTSTR lpszAppName)
	:CWinApp(lpszAppName)
{
	m_dwCommCtrlMjr = 0;
	m_dwCommCtrlMin = 0;
	m_hSystemImageList = NULL;
	m_sizSmallSystemIcon.cx = 16;
	m_sizSmallSystemIcon.cy = 16;
	m_iDfltImageListColorFlags = ILC_COLOR;
}


CemuleApp theApp(_T("eMule"));


// Workaround for buggy 'AfxSocketTerm' (needed at least for MFC 7.0)
#if _MFC_VER==0x0700 || _MFC_VER==0x0710
void __cdecl __AfxSocketTerm()
{
	_AFX_SOCK_STATE* pState = _afxSockState.GetData();
	if (pState->m_pfnSockTerm != NULL){
		VERIFY( WSACleanup() == 0 );
		pState->m_pfnSockTerm = NULL;
	}
}
#else
#error "You are using an MFC version which may require a special version of the above function!"
#endif

// CemuleApp Initialisierung

BOOL CemuleApp::InitInstance()
{
#ifdef _DEBUG
	oldMemState.Checkpoint();
#endif
	//afxMemDF = allocMemDF | delayFreeMemDF;

#ifdef _DUMP
	MiniDumper dumper(CURRENT_VERSION_LONG);
#endif

	AfxOleInit();

	pendinglink = 0;
	if (ProcessCommandline())
		return false;
	// InitCommonControls() ist für Windows XP erforderlich, wenn ein Anwendungsmanifest
	// die Verwendung von ComCtl32.dll Version 6 oder höher zum Aktivieren
	// von visuellen Stilen angibt. Ansonsten treten beim Erstellen von Fenstern Fehler auf.
	InitCommonControls();
	AtlGetCommCtrlVersion(&m_dwCommCtrlMjr, &m_dwCommCtrlMin);
	m_sizSmallSystemIcon.cx = GetSystemMetrics(SM_CXSMICON);
	m_sizSmallSystemIcon.cy = GetSystemMetrics(SM_CYSMICON);

	m_iDfltImageListColorFlags = GetAppImageListColorFlag();

	// don't use 32bit color resources if not supported by commctl
	if (m_iDfltImageListColorFlags == ILC_COLOR32 && m_dwCommCtrlMjr < 6)
		m_iDfltImageListColorFlags = ILC_COLOR16;
	// don't use >8bit color resources with OSs with restricted memory for GDI resources
	if (afxData.bWin95)
		m_iDfltImageListColorFlags = ILC_COLOR8;

	CWinApp::InitInstance();

	if (!AfxSocketInit())
	{
		AfxMessageBox(IDS_SOCKETS_INIT_FAILED);
		return FALSE;
	}
#if _MFC_VER==0x0700 || _MFC_VER==0x0710
	atexit(__AfxSocketTerm);
#else
#error "You are using an MFC version which may require a special version of the above function!"
#endif
	AfxEnableControlContainer();
	CemuleDlg dlg;
	emuledlg = &dlg;
	m_pMainWnd = &dlg;

	// create & initalize all the important stuff 
	glob_prefs = new CPreferences();
#ifdef _DEBUG
	_sntprintf(_szCrtDebugReportFilePath, ARRSIZE(_szCrtDebugReportFilePath), "%s\\%s", glob_prefs->GetAppDir(), APP_CRT_DEBUG_LOG_FILE);
#endif

	// Barry - Auto-take ed2k links
	if (glob_prefs->AutoTakeED2KLinks())
		Ask4RegFix(false, true);
	
    uploadBandwidthThrottler = new UploadBandwidthThrottler();
	clientlist = new CClientList();
	//clientcredits = new CClientCreditsList(glob_prefs);
	friendlist = new CFriendList();
	searchlist = new CSearchList();
	knownfiles = new CKnownFileList(glob_prefs->GetAppDir());
	serverlist = new CServerList(glob_prefs);
	serverconnect = new CServerConnect(serverlist,theApp.glob_prefs);
	sharedfiles = new CSharedFileList(glob_prefs,serverconnect,knownfiles);
	listensocket = new CListenSocket(glob_prefs);
	clientudp	= new CClientUDPSocket();
	clientcredits = new CClientCreditsList(glob_prefs); // moved up before friendlist to have credits when friends are activated.
	downloadqueue = new CDownloadQueue(glob_prefs,sharedfiles);	// bugfix - do this before creating the uploadqueue
	uploadqueue = new CUploadQueue(glob_prefs);
	ipfilter 	= new CIPFilter();
	webserver = new CWebServer(); // Webserver [kuchin]
	scheduler = new CScheduler();
	
	// reset statistic values
	theApp.stat_sessionReceivedBytes=0;
	theApp.stat_sessionSentBytes=0;
    theApp.stat_sessionSentBytesToFriend = 0;

    theApp.stat_reconnects=0;
	theApp.stat_transferStarttime=0;
	theApp.stat_serverConnectTime=0;
	theApp.stat_filteredclients=0;

	INT_PTR nResponse = dlg.DoModal();

	::CloseHandle(m_hMutexOneInstance);
#ifdef _DEBUG
	newMemState.Checkpoint();
	if (diffMemState.Difference(oldMemState, newMemState))
	{
		TRACE("Memory usage:\n");
		diffMemState.DumpStatistics();
	}
	//_CrtDumpMemoryLeaks();
#endif //_DEBUG

	emuledlg = NULL;
	return FALSE;
}

#ifdef _DEBUG
int CrtDebugReportCB(int reportType, char* message, int* returnValue)
{
	FILE* fp = fopen(_szCrtDebugReportFilePath, "a");
	if (fp){
		time_t tNow = time(NULL);
		char szTime[40];
		strftime(szTime, ARRSIZE(szTime), "%H:%M:%S", localtime(&tNow));
		fprintf(fp, "%s  %u  %s", szTime, reportType, message);
		fclose(fp);
	}
	*returnValue = 0; // avoid invokation of 'AfxDebugBreak' in ASSERT macros
	return TRUE; // avoid further processing of this debug report message by the CRT
}
#endif

bool CemuleApp::ProcessCommandline()
{
#ifdef _DEBUG
	for (int i = 1; i < __argc; i++){
		LPCTSTR pszParam = __targv[i];
		if (pszParam[0] == _T('-') || pszParam[0] == _T('/')){
			pszParam++;
			if (_tcscmp(pszParam, _T("assertfile")) == 0)
				_CrtSetReportHook(CrtDebugReportCB);
		}
	}
#endif

	CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

    m_hMutexOneInstance = ::CreateMutex(NULL, FALSE,_T(EMULE_GUID));
    bool bAlreadyRunning = ( ::GetLastError() == ERROR_ALREADY_EXISTS ||::GetLastError() == ERROR_ACCESS_DENIED);
	HWND maininst = NULL;
    if ( bAlreadyRunning ) EnumWindows(SearchEmuleWindow, (LPARAM)&maininst);

    if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileOpen) {
		CString command = cmdInfo.m_strFileName;
		if (command.Find("://")>0) {
			sendstruct.cbData = command.GetLength()+1; 
			sendstruct.dwData = OP_ED2KLINK; 
			sendstruct.lpData = command.GetBuffer(); 
    		if (maininst){
      			SendMessage(maininst,WM_COPYDATA,(WPARAM)0,(LPARAM) (PCOPYDATASTRUCT) &sendstruct); 
      			return true; 
			} 
    		else 
      			pendinglink = new CString(command);
		} else {
			sendstruct.cbData = command.GetLength()+1; 
			sendstruct.dwData = OP_CLCOMMAND;
			sendstruct.lpData = command.GetBuffer(); 
    		if (maininst){
      			SendMessage(maininst,WM_COPYDATA,(WPARAM)0,(LPARAM) (PCOPYDATASTRUCT) &sendstruct); 
      			return true; 
			}
		}
    }
    return (maininst || bAlreadyRunning);
}

BOOL CALLBACK CemuleApp::SearchEmuleWindow(HWND hWnd, LPARAM lParam){
	DWORD dwMsgResult;
	LRESULT res = ::SendMessageTimeout(hWnd,UWM_ARE_YOU_EMULE,0, 0,SMTO_BLOCK |SMTO_ABORTIFHUNG,10000,&dwMsgResult);
	if(res == 0)
		return TRUE;
	if(dwMsgResult == UWM_ARE_YOU_EMULE){ 
		HWND * target = (HWND *)lParam;
		*target = hWnd;
		return FALSE; 
	} 
	return TRUE; 
} 


void CemuleApp::UpdateReceivedBytes(int32 bytesToAdd) {
	SetTimeOnTransfer();
	stat_sessionReceivedBytes+=bytesToAdd;
}

void CemuleApp::UpdateSentBytes(int32 bytesToAdd, bool sentToFriend) {
	SetTimeOnTransfer();

    if(sentToFriend == false) {
	    stat_sessionSentBytes += bytesToAdd;
    } else {
	    stat_sessionSentBytesToFriend += bytesToAdd;
    }
}

void CemuleApp::SetTimeOnTransfer() {
	if (stat_transferStarttime>0) return;
	
	stat_transferStarttime=GetTickCount();
}

CString CemuleApp::StripInvalidFilenameChars(CString strText, bool bKeepSpaces)
{
	LPTSTR pszBuffer = strText.GetBuffer();
	LPTSTR pszSource = pszBuffer;
	LPTSTR pszDest = pszBuffer;

	while (*pszSource != '\0')
	{
		if (!((*pszSource <= 31 && *pszSource >= 0)	|| // lots of invalid chars for filenames in windows :=)
			*pszSource == '\"' || *pszSource == '*' || *pszSource == '<'  || *pszSource == '>' ||
			*pszSource == '?'  || *pszSource == '|' || *pszSource == '\\' || *pszSource == '/' || 
			*pszSource == ':') )
		{
			if (!bKeepSpaces && *pszSource == ' ')
				*pszDest = '.';
			*pszDest = *pszSource;
			pszDest++;
		}
		pszSource++;
	}
	*pszDest = '\0';
	strText.ReleaseBuffer();
	return strText;
}

CString CemuleApp::CreateED2kLink( CAbstractFile* f )
{
	CString strLink;
	strLink.Format("ed2k://|file|%s|%u|%s|/",
		StripInvalidFilenameChars(f->GetFileName(), false),	// spaces to dots
		f->GetFileSize(),
		EncodeBase16(f->GetFileHash(),16)
		);
	return strLink;
}

CString CemuleApp::CreateED2kSourceLink( CAbstractFile* f )
{
	if (!serverconnect->IsConnected() || serverconnect->IsLowID()){
		emuledlg->AddLogLine(true,GetResString(IDS_SOURCELINKFAILED));
		return CString("");
	}
	uint32 dwID = serverconnect->GetClientID();

	CString strLink;
	strLink.Format("ed2k://|file|%s|%u|%s|/|sources,%i.%i.%i.%i:%i|/",
		StripInvalidFilenameChars(f->GetFileName(), false),	// spaces to dots
		f->GetFileSize(),
		EncodeBase16(f->GetFileHash(),16),
		(uint8)dwID,(uint8)(dwID>>8),(uint8)(dwID>>16),(uint8)(dwID>>24), glob_prefs->GetPort() );
	return strLink;
}

CString CemuleApp::CreateHTMLED2kLink( CAbstractFile* f )
{
	CString strCode = "<a href=\"" + CreateED2kLink(f) + "\">" + StripInvalidFilenameChars(f->GetFileName(), true) + "</a>";
	return strCode;
}

bool CemuleApp::CopyTextToClipboard( CString strText )
{
	//allocate global memory & lock it
	HGLOBAL hGlobal = GlobalAlloc(GHND|GMEM_SHARE,strText.GetLength() + 1);
	if(hGlobal == NULL)
		return false;

	PTSTR pGlobal = static_cast<PTSTR>(GlobalLock(hGlobal));
	if( pGlobal == NULL ){
		GlobalFree(hGlobal);
		return false;
	}

	//copy the text
	strcpy(pGlobal,(LPCTSTR)strText);
	GlobalUnlock(hGlobal);

	//Open the Clipboard and insert the handle into the global memory
	bool bResult = false;
	if( OpenClipboard(NULL) )
	{
		if( EmptyClipboard() )
			bResult = (SetClipboardData(CF_TEXT,hGlobal) != NULL);
		CloseClipboard();
	}
	if (bResult)
		theApp.emuledlg->searchwnd.IgnoreClipBoardLinks(strText); // this is so eMule won't think the clipboard has ed2k links for adding
	else
		GlobalFree(hGlobal);
	return bResult;
}

// Get text from clipboard [enkeyDEV(Ottavio84)]
CString CemuleApp::CopyTextFromClipboard() 
{
	HGLOBAL	hglb; 
	LPTSTR  lptstr; 
	CString	retstring;
	if (!IsClipboardFormatAvailable(CF_TEXT)) 
		return ""; 
	if (!OpenClipboard(NULL)) 
		return ""; 

	hglb = GetClipboardData(CF_TEXT); 
	if (hglb != NULL) 
	{ 
		lptstr = (LPTSTR)GlobalLock(hglb); 
		if (lptstr != NULL) retstring = lptstr;
	} 
	CloseClipboard();
	
	return retstring;
}

void CemuleApp::OnlineSig() // Added By Bouc7 
{ 
	if (!theApp.glob_prefs->IsOnlineSignatureEnabled()) return;

    char* fullpath = new char[strlen(glob_prefs->GetAppDir())+MAX_PATH]; 
    sprintf(fullpath,"%sonlinesig.dat",glob_prefs->GetAppDir()); 
    CFile file; 
    if (!file.Open(fullpath,CFile::modeCreate|CFile::modeReadWrite)){ 
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERROR_SAVEFILE)+CString(" OnlineSig.dat"));
	    delete[] fullpath; 
		return;
    } 
    char buffer[20]; 
    if (serverconnect->IsConnected()) 
    { 
      file.Write("1",1); 
      file.Write("|",1); 
      file.Write(serverconnect->GetCurrentServer()->GetListName(),strlen(serverconnect->GetCurrentServer()->GetListName())); 
      // Not : file.Write(serverconnect->GetCurrentServer()->GetListName(),strlen(serverconnect->GetCurrentServer()- >GetRealName())); 

      file.Write("|",1); 
      file.Write(serverconnect->GetCurrentServer()->GetFullIP(),strlen(serverconnect->GetCurrentServer()->GetFullIP())); 
      file.Write("|",1); 
      itoa(serverconnect->GetCurrentServer()->GetPort(),buffer,10); 
      file.Write(buffer,strlen(buffer)); 
    } 
    else 
      file.Write("0",1); 

    file.Write("\n",1); 
    sprintf(buffer,"%.1f",(float)downloadqueue->GetDatarate()/1024); 
    file.Write(buffer,strlen(buffer)); 
    file.Write("|",1); 
    sprintf(buffer,"%.1f",(float)uploadqueue->GetDatarate()/1024); 
    file.Write(buffer,strlen(buffer)); 
    file.Write("|",1); 
    itoa(uploadqueue->GetWaitingUserCount(),buffer,10); 
    file.Write(buffer,strlen(buffer)); 

    file.Close(); 
    delete[] fullpath; 
    fullpath=NULL;
} //End Added By Bouc7

void CemuleApp::OnHelp() {

	// Change extension for help file
	CString strHelpFile = m_pszHelpFilePath;
	CFileFind ff;

	if (glob_prefs->GetLanguageID()!=MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT)) {
		int pos=strHelpFile.ReverseFind('.');
		CString temp;
		temp.Format("%s.%u.chm",strHelpFile.Left(pos),glob_prefs->GetLanguageID());
		if (pos>0) strHelpFile=temp;
		
		// if not exists, use original help (english)
		if (!ff.FindFile(strHelpFile, 0)) strHelpFile = m_pszHelpFilePath;
	}
	strHelpFile.Replace(".HLP", ".chm");

	// lets just open the helpfile by associated program, instead of more windows-dependency :)
	if (ff.FindFile(strHelpFile, 0)) ShellOpenFile(strHelpFile); else AfxMessageBox(GetResString(IDS_ERR_NOHELP)+"\n"+strHelpFile, MB_OK | MB_ICONERROR);
	ff.Close();
}

int CemuleApp::GetFileTypeSystemImageIdx(LPCTSTR pszFilePath, int iLength /* = -1 */)
{
	//TODO: This has to be MBCS aware..
	DWORD dwFileAttributes;
	LPCTSTR pszCacheExt = NULL;
	if (iLength == -1)
		iLength = _tcslen(pszFilePath);
	if (iLength > 0 && (pszFilePath[iLength - 1] == _T('\\') || pszFilePath[iLength - 1] == _T('/'))){
		// it's a directory
		pszCacheExt = _T("\\");
		dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
	}
	else{
		dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
		// search last '.' character *after* the last '\\' character
		for (int i = iLength - 1; i >= 0; i--){
			if (pszFilePath[i] == _T('\\') || pszFilePath[i] == _T('/'))
				break;
			if (pszFilePath[i] == _T('.')) {
				// point to 1st character of extension (skip the '.')
				pszCacheExt = &pszFilePath[i+1];
				break;
			}
		}
		if (pszCacheExt == NULL)
			pszCacheExt = _T("");	// empty extension
	}

	// Search extension in "ext->idx" cache.
	LPVOID vData;
	if (!m_aExtToSysImgIdx.Lookup(pszCacheExt, vData)){
		// Get index for the system's small icon image list
		SHFILEINFO sfi;
		DWORD dwResult = SHGetFileInfo(pszFilePath, dwFileAttributes, &sfi, sizeof(sfi),
									   SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
		if (dwResult == 0)
			return 0;
		ASSERT( m_hSystemImageList == NULL || m_hSystemImageList == (HIMAGELIST)dwResult );
		m_hSystemImageList = (HIMAGELIST)dwResult;

		// Store icon index in local cache
		m_aExtToSysImgIdx.SetAt(pszCacheExt, (LPVOID)sfi.iIcon);
		return sfi.iIcon;
	}
	// Return already cached value
	// Elandal: Assumes sizeof(void*) == sizeof(int)
	return (int)vData;
}
