//this file is part of eMule
//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.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.
#include "stdafx.h"
#include "emule.h"
#include "FriendListCtrl.h"
#include "friend.h"
#include "ClientDetailDialog.h"
#include "Addfriend.h"
#include "FriendList.h"
#include "emuledlg.h"
#include "ClientList.h"
#include "OtherFunctions.h"
#include "UpDownClient.h"
#include "ListenSocket.h"
#include "MenuCmds.h"
#include "ChatWnd.h"
// MORPH START - Added by Commander, Friendlinks [emulEspaa] - added by zz_fly
#include "ED2KLink.h"
// MORPH END - Added by Commander, Friendlinks [emulEspaa]

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


IMPLEMENT_DYNAMIC(CFriendListCtrl, CMuleListCtrl)

BEGIN_MESSAGE_MAP(CFriendListCtrl, CMuleListCtrl)
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnClick)
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnNmDblClk)
	ON_WM_CONTEXTMENU()
	ON_WM_SYSCOLORCHANGE()
END_MESSAGE_MAP()

CFriendListCtrl::CFriendListCtrl()
{
	SetGeneralPurposeFind(true);
	SetSkinKey(L"FriendsLv");
}

CFriendListCtrl::~CFriendListCtrl()
{
}

void CFriendListCtrl::Init()
{
	SetPrefsKey(_T("FriendListCtrl"));

	SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);

	RECT rcWindow;
	GetWindowRect(&rcWindow);
	InsertColumn(0, GetResString(IDS_QL_USERNAME), LVCFMT_LEFT, rcWindow.right - rcWindow.left - 4);

	SetAllIcons();
	theApp.friendlist->SetWindow(this);
	LoadSettings();
	SetSortArrow();
}

void CFriendListCtrl::OnSysColorChange()
{
	CMuleListCtrl::OnSysColorChange();
	SetAllIcons();
}

void CFriendListCtrl::SetAllIcons()
{
	CImageList iml;
	iml.Create(16, 16, theApp.m_iDfltImageListColorFlags | ILC_MASK, 0, 1);
	iml.Add(CTempIconLoader(_T("FriendNoClient")));
	iml.Add(CTempIconLoader(_T("FriendWithClient")));
	iml.Add(CTempIconLoader(_T("FriendConnected")));
	//Xman friend visualization
	iml.Add(CTempIconLoader(_T("FriendNoClientSlot")));
	iml.Add(CTempIconLoader(_T("FriendWithClientSlot")));
	iml.Add(CTempIconLoader(_T("FriendConnectedSlot")));
	//Xman end

	ASSERT( (GetStyle() & LVS_SHAREIMAGELISTS) == 0 );
	HIMAGELIST himlOld = ApplyImageList(iml.Detach());
	if (himlOld)
		ImageList_Destroy(himlOld);
}

void CFriendListCtrl::Localize()
{
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	HDITEM hdi;
	hdi.mask = HDI_TEXT;
	CString strRes;

	strRes = GetResString(IDS_QL_USERNAME);
	hdi.pszText = const_cast<LPTSTR>((LPCTSTR)strRes);
	pHeaderCtrl->SetItem(0, &hdi);

	/*size_t iItems = GetItemCount();// X: [CI] - [Code Improvement]
	for (size_t i = 0; i < iItems; i++)
		UpdateFriend(i, (CFriend*)GetItemData(i));*/
}

void CFriendListCtrl::UpdateFriend(int iItem, const CFriend* pFriend)
{
	//Xman CodeFix
	if (!CemuleDlg::IsRunning())
		return;
	//Xman end
	SetItemText(iItem, 0, pFriend->m_strName);

	int iImage;
    if (!pFriend->GetLinkedClient())
		iImage = 0;
	else if (pFriend->GetLinkedClient()->socket && pFriend->GetLinkedClient()->socket->IsConnected())
		iImage = 2;
	else
		iImage = 1;

	//Xman friend visualization
	if (pFriend->GetFriendSlot()) iImage += 3;
	//Xman end
	SetItem(iItem, 0, LVIF_IMAGE, 0, iImage, 0, 0, 0, 0);
}

void CFriendListCtrl::AddFriend(const CFriend* pFriend)
{
	//Xman CodeFix
	if (!CemuleDlg::IsRunning())
		return;
	//Xman end
	int iItem = InsertItem(LVIF_TEXT | LVIF_PARAM, GetItemCount(), pFriend->m_strName, 0, 0, 0, (LPARAM)pFriend);
	if (iItem >= 0)
		UpdateFriend(iItem, pFriend);
	theApp.emuledlg->chatwnd->UpdateFriendlistCount(theApp.friendlist->GetCount());
}

void CFriendListCtrl::RemoveFriend(const CFriend* pFriend)
{
	//Xman CodeFix
	if (!CemuleDlg::IsRunning())
		return;
	//Xman end
	LVFINDINFO find;
	find.flags = LVFI_PARAM;
	find.lParam = (LPARAM)pFriend;
	int iItem = FindItem(&find);
	if (iItem != -1)
		DeleteItem(iItem);
	theApp.emuledlg->chatwnd->UpdateFriendlistCount(theApp.friendlist->GetCount());
}

void CFriendListCtrl::RefreshFriend(const CFriend* pFriend)
{
	//Xman CodeFix
	if (!CemuleDlg::IsRunning())
		return;
	//Xman end

	LVFINDINFO find;
	find.flags = LVFI_PARAM;
	find.lParam = (LPARAM)pFriend;
	int iItem = FindItem(&find);
	if (iItem != -1)
		UpdateFriend(iItem, pFriend);
}

void CFriendListCtrl::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
{
	if (point.x != -1 || point.y != -1) {// X: [BF] - [Bug Fix]
		RECT rcClient;
		GetClientRect(&rcClient);
		ClientToScreen(&rcClient);
		if (!PtInRect(&rcClient,point)) {
			Default();
			return;
		}
	}
	CTitleMenu ClientMenu;
	ClientMenu.CreatePopupMenu();
	ClientMenu.AddMenuTitle(GetResString(IDS_FRIENDLIST), true);

	const CFriend* cur_friend = NULL;
	int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
	if (iSel != -1) {
		cur_friend = (CFriend*)GetItemData(iSel);
		ClientMenu.AppendMenu(MF_STRING,MP_DETAIL, GetResString(IDS_SHOWDETAILS));
		ClientMenu.SetDefaultItem(MP_DETAIL);
	}

	ClientMenu.AppendMenu(MF_STRING, MP_ADDFRIEND, GetResString(IDS_ADDAFRIEND));
	ClientMenu.AppendMenu(MF_STRING | (cur_friend ? MF_ENABLED : MF_GRAYED), MP_REMOVEFRIEND, GetResString(IDS_REMOVEFRIEND));
	ClientMenu.AppendMenu(MF_STRING | (cur_friend ? MF_ENABLED : MF_GRAYED), MP_MESSAGE, GetResString(IDS_SEND_MSG));
	ClientMenu.AppendMenu(MF_STRING | ((cur_friend==NULL || (cur_friend && cur_friend->GetLinkedClient(true) && !cur_friend->GetLinkedClient(true)->GetViewSharedFilesSupport())) ? MF_GRAYED : MF_ENABLED), MP_SHOWLIST, GetResString(IDS_VIEWFILES));
	ClientMenu.AppendMenu((cur_friend ? (cur_friend->GetFriendSlot() ? MF_CHECKED : MF_UNCHECKED) : MF_GRAYED), MP_FRIENDSLOT, GetResString(IDS_FRIENDSLOT));
	ClientMenu.AppendMenu(MF_STRING | (GetItemCount() > 0 ? MF_ENABLED : MF_GRAYED), MP_FIND, GetResString(IDS_FIND));

	// MORPH START - Modified by Commander, Friendlinks [emulEspaa] - added by zz_fly
	ClientMenu.AppendMenu(MF_SEPARATOR);
    ClientMenu.AppendMenu(MF_STRING | (theApp.IsEd2kFriendLinkInClipboard() ? MF_ENABLED : MF_GRAYED), MP_PASTE, GetResString(IDS_PASTE));
	ClientMenu.AppendMenu(MF_STRING | (cur_friend ? MF_ENABLED : MF_GRAYED), MP_GETFRIENDED2KLINK, GetResString(IDS_GETFRIENDED2KLINK));
	ClientMenu.AppendMenu(MF_STRING, MP_GETMYFRIENDED2KLINK, GetResString(IDS_GETMYFRIENDED2KLINK));
	// MORPH END - Modified by Commander, Friendlinks [emulEspaa]
	// - show requested files (sivka/Xman)
	ClientMenu.AppendMenu(MF_SEPARATOR); 
	ClientMenu.AppendMenu(MF_STRING | (cur_friend && cur_friend->GetLinkedClient() ? MF_ENABLED : MF_GRAYED),MP_LIST_REQUESTED_FILES, GetResString(IDS_LISTREQUESTED)); 
	//Xman end
	GetPopupMenuPos(*this, point);
	ClientMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
	VERIFY( ClientMenu.DestroyMenu() );
}

BOOL CFriendListCtrl::OnCommand(WPARAM wParam, LPARAM /*lParam*/)
{
	wParam = LOWORD(wParam);

	CFriend* cur_friend = NULL;
	int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
	if (iSel != -1) 
		cur_friend = (CFriend*)GetItemData(iSel);
	
	switch (wParam)
	{
		case MP_MESSAGE:
			if (cur_friend)
				theApp.emuledlg->chatwnd->StartSession(cur_friend->GetClientForChatSession());
			break;
		case MP_REMOVEFRIEND:
			if (cur_friend)
			{
				theApp.friendlist->RemoveFriend(cur_friend);
				// auto select next item after deleted one.
				if ((size_t)iSel < GetItemCount()) {
					SetSelectionMark(iSel);
					SetItemState(iSel, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
				}
				theApp.emuledlg->chatwnd->UpdateSelectedFriendMsgDetails();
			}
			break;
		case MP_ADDFRIEND:{
			// NEO: MLD - [ModelesDialogs] -- Xanatos -->
			CAddFriend* dlg = new CAddFriend();
			dlg->OpenDialog(); 
			// NEO: MLD END <-- Xanatos --
			//CAddFriend dialog2; 
			//dialog2.DoModal();
			break;
		}
		case MP_DETAIL:
		case MPG_ALTENTER:
		case IDA_ENTER:
			if (cur_friend)
				ShowFriendDetails(cur_friend);
			break;
		case MP_SHOWLIST:
			if (cur_friend)
			{
				if (cur_friend->GetLinkedClient(true))
					cur_friend->GetLinkedClient()->RequestSharedFileList();
				else
				{
					CUpDownClient* newclient = new CUpDownClient(0, cur_friend->m_nLastUsedPort, cur_friend->m_dwLastUsedIP, 0, 0, true);
//>>> WiZaRd::Easy ModVersion
					//Short note: because of the overhead saving code in baseclient by Morph 
					//const bool bSend = !m_pszUsername || !m_strModVersion.IsEmpty();
					//we must ensure that the name for a real new client is really NULL
//					newclient->SetUserName(cur_friend->m_strName);
//<<< WiZaRd::Easy ModVersion
					newclient->SetUserHash(cur_friend->m_abyUserhash); //>>> WiZaRd::FiX - missing in official?
					//Xman Code Improvement don't search new generated clients in lists
					/*
					theApp.clientlist->AddClient(newclient);
					*/
					theApp.clientlist->AddClient(newclient,true);
					//Xman end
					newclient->RequestSharedFileList();
				}
			}
			break;
		case MP_FRIENDSLOT:
			if (cur_friend)
			{
				bool bIsAlready = cur_friend->GetFriendSlot();
				theApp.friendlist->RemoveAllFriendSlots();
				if (!bIsAlready)
				{ //Xman
					cur_friend->SetFriendSlot(true);
					//Xman friend visualization
					UpdateFriend(iSel,cur_friend);
					//Xman end
				} //Xman
			}
			break;

			// - show requested files (sivka/Xman)
		case MP_LIST_REQUESTED_FILES:
			{ 
				if (cur_friend && cur_friend->GetLinkedClient())
				{
					cur_friend->GetLinkedClient()->ShowRequestedFiles(); 
				}
				break;
			}
			//Xman end
		// MORPH START - Added by Commander, Friendlinks [emulEspaa] - added by zz_fly
		case MP_PASTE:
		{
			if(theApp.IsEd2kFriendLinkInClipboard()){
				CString link = theApp.CopyTextFromClipboard();
				link.Trim();
				if ( link.IsEmpty() )
					break;
				try{
					CED2KLink* pLink = CED2KLink::CreateLinkFromUrl(link);
					if (pLink){
						if(pLink->GetKind() == CED2KLink::kFriend ){
							// Better with dynamic_cast, but no RTTI enabled in the project
							CED2KFriendLink* pFriendLink = static_cast<CED2KFriendLink*>(pLink);
							uchar userHash[16];
							pFriendLink->GetUserHash(userHash);

							if (theApp.friendlist->SearchFriend(userHash,0,0) == NULL )
								theApp.friendlist->AddFriend(userHash, 0, 0, 0, 0, pFriendLink->GetUserName(), 1);
						}
						delete pLink;
					}
				}
				catch(CString strError){
					AfxMessageBox(strError);
				}
			}
		}
			break;
        case MP_GETFRIENDED2KLINK:
		{
			if ( cur_friend && cur_friend->HasUserhash() ){
				CString sLink;
				sLink.Format(_T("ed2k://|friend|%s|%s|/"),
					cur_friend->m_strName,
					EncodeBase16(cur_friend->m_abyUserhash,16));
				theApp.CopyTextToClipboard(sLink);
			}
		}
			break;
        case MP_GETMYFRIENDED2KLINK:
		{
			CString sLink;
			sLink.Format(_T("ed2k://|friend|%s|%s|/"),
				thePrefs.GetUserNick(),
				EncodeBase16(thePrefs.GetUserHash(),16));
			theApp.CopyTextToClipboard(sLink);
		}
			break;
		// MORPH END - Added by Commander, Friendlinks [emulEspaa]

		case MP_FIND:
			OnFindStart();
			break;
	}
	return true;
}

void CFriendListCtrl::OnNmDblClk(NMHDR* /*pNMHDR*/, LRESULT* pResult)
{
	int iSel = GetNextItem(-1, LVIS_SELECTED | LVIS_FOCUSED);
	if (iSel != -1) 
		ShowFriendDetails((CFriend*)GetItemData(iSel));
	*pResult = 0;
}

void CFriendListCtrl::ShowFriendDetails(const CFriend* pFriend)
{
	if (pFriend)
	{
		if (pFriend->GetLinkedClient(true))
		{
			CClientDetailDialog dialog(pFriend->GetLinkedClient());
			dialog.DoModal();
		}
		else
		{
			// NEO: MLD - [ModelesDialogs] -- Xanatos -->
			CAddFriend* dlg = new CAddFriend();
			dlg->m_pShowFriend = const_cast<CFriend*>(pFriend);
			dlg->OpenDialog(); 
			// NEO: MLD END <-- Xanatos --
			//CAddFriend dlg;
			//dlg.m_pShowFriend = const_cast<CFriend*>(pFriend);
			//dlg.DoModal();
		}
	}
}

BOOL CFriendListCtrl::PreTranslateMessage(MSG* pMsg) 
{
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE)
		PostMessage(WM_COMMAND, MP_REMOVEFRIEND, 0);
	else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_INSERT)
		PostMessage(WM_COMMAND, MP_ADDFRIEND, 0);

	return CMuleListCtrl::PreTranslateMessage(pMsg);
}

void CFriendListCtrl::OnLvnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLISTVIEW *pNMListView = (NMLISTVIEW *)pNMHDR;

	// Determine ascending based on whether already sorted on this column
	int iSortItem = GetSortItem();
	bool bOldSortAscending = GetSortAscending();
	bool bSortAscending = (iSortItem != pNMListView->iSubItem) ? true : !bOldSortAscending;

	// Item is column clicked
	iSortItem = pNMListView->iSubItem;

	// Sort table
	SetSortArrow(iSortItem, bSortAscending);
	SortItems(SortProc, MAKELONG(iSortItem, (bSortAscending ? 0 : 0x0001)));

	*pResult = 0;
}

int CFriendListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	const CFriend *item1 = (CFriend *)lParam1;
	const CFriend *item2 = (CFriend *)lParam2; 
	if (item1 == NULL || item2 == NULL)
		return 0;

	//Xman 
	//sometimes I still receive crashdumps poiting to the CompareLocaleStringNoCase
	//but this line may not crash, because items are not NULL and an empty String can't be the problem
	//one user reported this happend when closing emule... maybe this part of code is called
	//after/during the friends are destroyed.. a simple test doesn't hurt
	if (!CemuleDlg::IsRunning())
		return 0;
	//Xman end

	int iResult;
	switch (LOWORD(lParamSort))
	{
		case 0:
			iResult = CompareLocaleStringNoCase(item1->m_strName, item2->m_strName);
			break;

		default:
			return 0;
	}
	if (HIWORD(lParamSort))
		iResult = -iResult;
	return iResult;
}

void CFriendListCtrl::UpdateList()
{
	theApp.emuledlg->chatwnd->UpdateFriendlistCount(theApp.friendlist->GetCount());
	SortItems(SortProc, MAKELONG(GetSortItem(), (GetSortAscending() ? 0 : 0x0001)));
}
