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


// SearchListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "emule.h"
#include "SearchListCtrl.h"
#include "otherfunctions.h"

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


// CSearchListCtrl

IMPLEMENT_DYNAMIC(CSearchListCtrl, CMuleListCtrl)
CSearchListCtrl::CSearchListCtrl() {
	searchlist = 0;  // i_a 
	m_nResultsID = 0; // i_a 
	m_iColumns = 0;
	m_bSetImageList = false;
	SetGeneralPurposeFind(true);
}

void CSearchListCtrl::Init(CSearchList* in_searchlist){
	SetExtendedStyle(LVS_EX_FULLROWSELECT);
	ModifyStyle(LVS_SINGLESEL,0);
	searchlist = in_searchlist;

	InsertColumn(0,GetResString(IDS_DL_FILENAME),LVCFMT_LEFT,250);
	InsertColumn(1,GetResString(IDS_DL_SIZE),LVCFMT_LEFT,100);
	InsertColumn(2,GetResString(IDS_DL_SOURCES),LVCFMT_LEFT,50);
	InsertColumn(3,GetResString(IDS_TYPE),LVCFMT_LEFT,65);
	InsertColumn(4,GetResString(IDS_FILEID),LVCFMT_LEFT,220);
	m_iColumns = 5; // NOTE: One column is created dynamically as needed!

	CreateMenues();

	LoadSettings(CPreferences::tableSearch);

	// Barry - Use preferred sort order from preferences
	int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableSearch);
	if (sortItem != -1){// don't force a sorting if '-1' is specified, so we can better see how the search results are arriving
		bool sortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableSearch);
		SetSortArrow(sortItem, sortAscending);
		SortItems(SortProc, sortItem + (sortAscending ? 0:10));
	}
}

CSearchListCtrl::~CSearchListCtrl(){
}

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

	strRes = GetResString(IDS_DL_FILENAME);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(0, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SIZE);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(1, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SOURCES);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(2, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_TYPE);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(3, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_FILEID);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(4, &hdi);
	strRes.ReleaseBuffer();

	CreateMenues();
}

BEGIN_MESSAGE_MAP(CSearchListCtrl, CMuleListCtrl)
	ON_NOTIFY_REFLECT ( NM_CUSTOMDRAW, OnCustomdraw )
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
	ON_WM_CONTEXTMENU()
END_MESSAGE_MAP()

// CSearchListCtrl message handlers

void CSearchListCtrl::AddResult(CSearchFile* toshow){

	// update tab-counter for the given searchfile
	if (theApp.emuledlg->searchwnd.searchselect.GetItemCount()>0) {
		CString csName,sourceStr;
		int j;
		char szText[510];

		TCITEM tabitem;
		tabitem.mask = TCIF_PARAM;
		tabitem.lParam = -1;

		int tabCounter;
		for (tabCounter = 0; tabCounter < theApp.emuledlg->searchwnd.searchselect.GetItemCount();tabCounter++){
			theApp.emuledlg->searchwnd.searchselect.GetItem(tabCounter,&tabitem);
			if (tabitem.lParam == toshow->GetSearchID())
				break;
		}

		if (tabitem.lParam == toshow->GetSearchID()) {
			tabitem.pszText = szText;
			tabitem.cchTextMax = sizeof szText;
			tabitem.mask = TCIF_TEXT;
			theApp.emuledlg->searchwnd.searchselect.GetItem(tabCounter,&tabitem);
			
			csName = szText;

			tabitem.mask = TCIF_PARAM;
			theApp.emuledlg->searchwnd.searchselect.GetItem(tabCounter,&tabitem);

			sourceStr.Format(" (%u)", searchlist->GetFoundFiles(toshow->GetSearchID()) );

			j=csName.ReverseFind('(');
			if (j>0) csName=csName.Left(j-1);
			csName.Append(sourceStr);

			tabitem.mask = TCIF_TEXT;
			tabitem.pszText = csName.LockBuffer();
			theApp.emuledlg->searchwnd.searchselect.SetItem(tabCounter,&tabitem);
			csName.UnlockBuffer();
		}
	}


	if (toshow->GetSearchID() != m_nResultsID)
		return;

	int iImage = theApp.GetFileTypeSystemImageIdx(toshow->GetFileName());
	if (!m_bSetImageList && theApp.GetSystemImageList() != NULL){
		ASSERT( GetStyle() & LVS_SHAREIMAGELISTS );
		ApplyImageList(theApp.GetSystemImageList());
		m_bSetImageList = true;
	}

	uint32 itemnr = GetItemCount();
	itemnr = InsertItem(LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE,itemnr,toshow->GetFileName(),0,0,iImage,(LPARAM)toshow);
	uint32 filesize = toshow->GetIntTagValue(FT_FILESIZE); 
	char cbuffer[50];
	SetItemText(itemnr,1,CastItoXBytes(filesize));
	itoa(toshow->GetIntTagValue(FT_SOURCES),cbuffer,10);
	if (toshow->GetClientsCount())
		_snprintf(cbuffer,sizeof cbuffer,"%s (%u)",cbuffer,toshow->GetClientsCount());
	SetItemText(itemnr,2,cbuffer);
	SetItemText(itemnr,3,GetFiletypeByName(toshow->GetFileName()));
	SetItemText(itemnr,4,md4str(toshow->GetFileHash()));
	if (toshow->GetDirectory()){
		if (m_iColumns < 6){
			InsertColumn(5,GetResString(IDS_FOLDER),LVCFMT_LEFT,220);
			m_iColumns++;
		}
		SetItemText(itemnr,5,toshow->GetDirectory());
	}
}

void CSearchListCtrl::UpdateSources(CSearchFile* toupdate){
	LVFINDINFO find;
	find.flags = LVFI_PARAM;
	find.lParam = (LPARAM)toupdate;
	int index = FindItem(&find);
	if (index != (-1)){
		char buffer[50];
		itoa(toupdate->GetSourceCount(),buffer,10);
		if (toupdate->GetClientsCount())
			_snprintf(buffer,sizeof buffer,"%s (%u)",buffer,toupdate->GetClientsCount());
		SetItemText(index,2,buffer);
		Update(index);
	}
}

void CSearchListCtrl::RemoveResult(CSearchFile* toremove){
	LVFINDINFO find;
	find.flags = LVFI_PARAM;
	find.lParam = (LPARAM)toremove;
	sint32 result = FindItem(&find);
	if(result != (-1) )
		this->DeleteItem(result);
}

void CSearchListCtrl::ShowResults(uint32 nResultsID){
	DeleteAllItems();
	m_nResultsID = nResultsID;
	searchlist->ShowResults(m_nResultsID);
}

void CSearchListCtrl::OnCustomdraw(NMHDR* pNMHDR, LRESULT* pResult ){
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );

    *pResult = CDRF_DODEFAULT;

    if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage){
        *pResult = CDRF_NOTIFYITEMDRAW;
    }
    else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage){
		COLORREF crText = ::GetSysColor(COLOR_WINDOWTEXT);
		uint32 red = GetRValue(crText);
		uint32 green = GetGValue(crText);
		uint32 blue = GetGValue(crText);

		CSearchFile* file = (CSearchFile*)pLVCD->nmcd.lItemlParam;
		if (!file)
			return;

		CKnownFile* sameFile = theApp.sharedfiles->GetFileByID(file->GetFileHash());
		if (!sameFile)
			sameFile = theApp.downloadqueue->GetFileByID(file->GetFileHash());
		if (!sameFile) // Show already downloaded files
			sameFile = theApp.knownfiles->FindKnownFileByID(file->GetFileHash());

		if (sameFile) {
			if (sameFile->IsPartFile()) {
				// Already downloading
				red = (file->GetSourceCount()+4) * 20;
				if (red > 255) red = 255;
			}
			else {
				// Already downloaded
				green = 128;
			}
		}
		else {
			blue = file->GetSourceCount()==0 ? 0 : ((file->GetSourceCount()-1) * 20);
			if (blue > 255) blue = 255;
		}

		pLVCD->clrText = RGB(red, green ,blue);
        *pResult = CDRF_DODEFAULT;
     }
}

void CSearchListCtrl::OnColumnClick( NMHDR* pNMHDR, LRESULT* pResult){
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

	// Barry - Store sort order in preferences
	// Determine ascending based on whether already sorted on this column
	int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableSearch);
	bool m_oldSortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableSearch);
	bool sortAscending = (sortItem != pNMListView->iSubItem) ? true : !m_oldSortAscending;

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

	// Save new preferences
	theApp.glob_prefs->SetColumnSortItem(CPreferences::tableSearch, sortItem);
	theApp.glob_prefs->SetColumnSortAscending(CPreferences::tableSearch, sortAscending);

	// Sort table
	SetSortArrow(sortItem, sortAscending);
	SortItems(SortProc, sortItem + (sortAscending ? 0:10));

	*pResult = 0;
}

int CSearchListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort){
	CSearchFile* item1 = (CSearchFile*)lParam1;
	CSearchFile* item2 = (CSearchFile*)lParam2;	
	switch(lParamSort){
		case 0: //filename asc
			return _tcsicmp(item1->GetFileName(),item2->GetFileName());
		case 10: //filename desc
			return _tcsicmp(item2->GetFileName(),item1->GetFileName());

		case 1: //size asc
			return CompareUnsigned(item1->GetIntTagValue(FT_FILESIZE), item2->GetIntTagValue(FT_FILESIZE));
		case 11: //size desc
			return CompareUnsigned(item2->GetIntTagValue(FT_FILESIZE), item1->GetIntTagValue(FT_FILESIZE));

		case 2: //sources asc
			return CompareUnsigned(item1->GetIntTagValue(FT_SOURCES), item2->GetIntTagValue(FT_SOURCES));
		case 12: //sources desc
			return CompareUnsigned(item2->GetIntTagValue(FT_SOURCES), item1->GetIntTagValue(FT_SOURCES));

		case 3: //type asc
			return GetFiletypeByName(item1->GetFileName()).Compare(GetFiletypeByName(item2->GetFileName()));

		case 13: //type  desc
			return GetFiletypeByName(item2->GetFileName()).Compare(GetFiletypeByName(item1->GetFileName()));

		case 4: //filahash asc
			return memcmp(item1->GetFileHash(),item2->GetFileHash(),16);
		case 14: //m_abyFileHash desc
			return memcmp(item2->GetFileHash(),item1->GetFileHash(),16);

		case 5: //path asc
			if (item1->GetDirectory() && item2->GetDirectory())
				return _tcsicmp(item1->GetDirectory(),item2->GetDirectory());
			if (item1->GetDirectory())
				return -1;
			if (item2->GetDirectory())
				return 1;
			return 0;
		case 15: //path desc
			if (item1->GetDirectory() && item2->GetDirectory())
				return -_tcsicmp(item1->GetDirectory(),item2->GetDirectory());
			if (item1->GetDirectory())
				return 1;
			if (item2->GetDirectory())
				return -1;
			return 0;

		default:
			return 0;
	}
}

void CSearchListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)
{
	UINT flag=(GetSelectionMark()!=(-1)) ? MF_ENABLED:MF_GRAYED;

	m_SearchFileMenu.EnableMenuItem(MP_RESUME,flag);
	m_SearchFileMenu.EnableMenuItem(MP_GETED2KLINK,flag);
	m_SearchFileMenu.EnableMenuItem(MP_GETHTMLED2KLINK,flag);

	int counter;
	CMenu m_Web;
	m_Web.CreateMenu();
	UpdateURLMenu(m_Web,counter);
	UINT flag2;
	flag2=(counter==0) ? MF_GRAYED:MF_STRING;
	
	m_SearchFileMenu.AppendMenu(flag2|MF_POPUP,(UINT_PTR)m_Web.m_hMenu, GetResString(IDS_WEBSERVICES) );
	
	m_SearchFileMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON,point.x,point.y,this);
	m_SearchFileMenu.RemoveMenu(9,MF_BYPOSITION);
	VERIFY( m_Web.DestroyMenu() );
}


BOOL CSearchListCtrl::OnCommand(WPARAM wParam, LPARAM lParam)
{
	CSearchFile* file ;
	int item;

	if (GetSelectionMark() != (-1)){
		file = (CSearchFile*)GetItemData(GetSelectionMark());
		if (wParam>=MP_WEBURL && wParam<=MP_WEBURL+256) {
			RunURL(file, theApp.webservices.GetAt(wParam-MP_WEBURL) );
		}
		switch (wParam){
			case MP_GETED2KLINK:
				{
					CString clpbrd;
					POSITION pos=this->GetFirstSelectedItemPosition();
					while (pos!=NULL) 
					{
						item = this->GetNextSelectedItem(pos); 
						clpbrd+=theApp.CreateED2kLink((CSearchFile*)this->GetItemData(item))+"\n";
					}
					theApp.CopyTextToClipboard(clpbrd);
					break;
				}
			case MP_GETHTMLED2KLINK:
				{
					CString clpbrd;
					POSITION pos=this->GetFirstSelectedItemPosition();
					while (pos!=NULL) 
					{
						item = this->GetNextSelectedItem(pos); 
						clpbrd+=theApp.CreateHTMLED2kLink((CSearchFile*)this->GetItemData(item))+"\n";
					}
					theApp.CopyTextToClipboard(clpbrd);
					break;
				}
			case MP_RESUME:
				{
					theApp.emuledlg->searchwnd.OnBnClickedSdownload();
					break;
				}
			case MP_REMOVEALL:
				{
					theApp.emuledlg->searchwnd.DeleteAllSearchs();
					break;
				}
			case MP_REMOVESELECTED:
				{
					SetRedraw(false);
					POSITION pos;
					while (GetFirstSelectedItemPosition()!=NULL) 
					{
						pos=GetFirstSelectedItemPosition();
						item = this->GetNextSelectedItem(pos); 
						theApp.searchlist->RemoveResults( (CSearchFile*)this->GetItemData(item) );
					}
					SetRedraw(true);
					break;
				}
		}
	}
	switch (wParam){
		case MP_REMOVE:
			{
				theApp.emuledlg->searchwnd.DeleteSearch(m_nResultsID);
				break;
			}
	}
	return true;
}

void CSearchListCtrl::OnLvnDeleteallitems(NMHDR *pNMHDR, LRESULT *pResult){
	// To suppress subsequent LVN_DELETEITEM notification messages, return TRUE.
	*pResult = TRUE;
}

void CSearchListCtrl::CreateMenues() {

	if (m_SearchFileMenu) VERIFY( m_SearchFileMenu.DestroyMenu() );

	m_SearchFileMenu.CreatePopupMenu();
	m_SearchFileMenu.AddMenuTitle(GetResString(IDS_FILE));
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_RESUME, GetResString(IDS_DOWNLOAD));
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_GETED2KLINK, GetResString(IDS_DL_LINK1));
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_GETHTMLED2KLINK, GetResString(IDS_DL_LINK2));
	m_SearchFileMenu.AppendMenu(MF_SEPARATOR);
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_REMOVESELECTED, GetResString(IDS_REMOVESELECTED));
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_REMOVE, GetResString(IDS_REMOVESEARCHSTRING));
	m_SearchFileMenu.AppendMenu(MF_STRING,MP_REMOVEALL, GetResString(IDS_REMOVEALLSEARCH));
	m_SearchFileMenu.SetDefaultItem(MP_RESUME);
	m_SearchFileMenu.AppendMenu(MF_SEPARATOR);

}