/***************************************************************************
                          dcfilebrowser.cpp  -  description
                             -------------------
    begin                : Fre Nov 29 2002
    copyright            : (C) 2002 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 <qapplication.h>
#include <q3header.h>
#include <qlabel.h>
#include <qcursor.h>
#include <QFileDialog>
#include <QUrl>
#include <qtoolbutton.h>
#include <qcombobox.h>
#include <qtooltip.h>
#include <qclipboard.h>
#include <qmessagebox.h>

#include <QMdiArea>

#include "dcmenuhandler.h"
#include "dcconfig.h"
#include "dciconloader.h"

#include "dcfilebrowser.h"
#include "dcevent.h"

#include <dclib/dcos.h>
#include <dclib/core/cstring.h>
#include <dclib/core/cdir.h>
#include <dclib/core/cxml.h>
#include <dclib/core/che3.h>
#include <dclib/core/cbz.h>
#include <dclib/cutils.h>

// for file type icons
#include <dclib/cfilemanager.h>

/** */
DCFileBrowser::DCFileBrowser( QWidget* parent, const char *name, Qt::WindowFlags wflags, bool viewonly )
	: QWidget( parent, name, wflags )
{
	setupUi(this);
	
	// set caption
	setCaption(tr("Filebrowser"));
	
	if ( (parent != 0) && (qobject_cast<QMdiArea*>(parent) != 0) && (!viewonly) )
	{
		(qobject_cast<QMdiArea*>(parent))->addSubWindow(this);
	}
	
	// set width mode
	ListView_DIRECTORY->setColumnWidthMode( 0, Q3ListView::Maximum );
	ListView_FILES->setColumnWidthMode( 0, Q3ListView::Maximum );
	ListView_FILES->setColumnWidthMode( 1, Q3ListView::Maximum );
	ListView_FILES->setColumnWidthMode( 2, Q3ListView::Maximum );
	ListView_FILES->setColumnWidthMode( 3, Q3ListView::Maximum );
	ListView_FILES->setColumnWidthMode( 4, Q3ListView::Maximum );

	// set right alignment on size columns
	ListView_DIRECTORY->setColumnAlignment( 1, Qt::AlignRight );
	ListView_FILES->setColumnAlignment( 1, Qt::AlignRight );
	ListView_FILES->setColumnAlignment( 2, Qt::AlignRight );
	
	// hide the column header - only if not using size column
	if ( g_pConfig->GetFolderSizesInLeftPane() == FALSE )
	{
		ListView_DIRECTORY->header()->hide();
	}

	m_nShareSize = 0;
	m_nFileCount = 0;

	m_pDecomp = 0;

	//if ( !viewonly )
	//{
		InitDocument();
	//}
}

/** */
DCFileBrowser::~DCFileBrowser()
{
	Q3ListViewItemIterator it( ListView_DIRECTORY );

	for ( ; it.current(); it++ )
	{
		// no longer have auto-delete with QT4 QList
		for ( int i = 0; i < ((DCFileBrowserListItem*)it.current())->m_pFileList.size(); i++ )
		{
			delete ((DCFileBrowserListItem*)it.current())->m_pFileList.at(i);
		}
		
		((DCFileBrowserListItem*)it.current())->m_pFileList.clear();
	}
	
	if ( m_pDecomp != 0 )
	{
		/* there is no way to stop CBZ::Decompress so we are a bit stuck */
		printf("~DCFileBrowser: waiting for thread to finish\n");
		m_pDecomp->wait();
		printf("~DCFileBrowser: thread finished, cleaning up\n");
		if ( m_pDecomp->getData() != 0 )
		{
			delete m_pDecomp->getData();
		}
		if ( m_pDecomp->recompressed != 0 )
		{
			delete m_pDecomp->recompressed;
		}
		delete m_pDecomp;
	}
}
                                                                                   
void DCFileBrowser::InitDocument()
{
	connect( ListView_DIRECTORY, SIGNAL(selectionChanged()), this, SLOT(slotCurrentChangedDirectory()) );
	connect( ListView_DIRECTORY, SIGNAL(expanded(Q3ListViewItem*)), this, SLOT(slotExpandedDirectory(Q3ListViewItem*)) );
	connect( ListView_DIRECTORY, SIGNAL(collapsed(Q3ListViewItem*)), this, SLOT(slotCollapsedDirectory(Q3ListViewItem*)) );
	connect( ListView_DIRECTORY, SIGNAL(contextMenuRequested( Q3ListViewItem *, const QPoint &, int )), this, SLOT(slotRightButtonClickedDirectory(Q3ListViewItem*, const QPoint &, int )) );
	connect( ListView_FILES, SIGNAL(contextMenuRequested( Q3ListViewItem *, const QPoint &, int )), this, SLOT(slotRightButtonClickedFiles(Q3ListViewItem*, const QPoint &, int )) );
	connect( ListView_FILES, SIGNAL(doubleClicked( Q3ListViewItem *, const QPoint &, int)), this, SLOT(slotItemDoubleClicked( Q3ListViewItem *, const QPoint &, int )) );

 	connect( ToolButton_OPEN, SIGNAL(clicked()), this, SLOT(slotFileOpen()) );
 	connect( ToolButton_SAVE, SIGNAL(clicked()), this, SLOT(slotFileSave()) );
}

/** */
bool DCFileBrowser::event( QEvent * event )
{
	if ( event->type() == EVENT_OPEN_FILELIST_FOLDER )
	{
		DCFileBrowserListItem * item = ((DC_OpenFilelistFolderEvent*)event)->m_pItem;
		
		ListView_DIRECTORY->clearSelection();
		ListView_DIRECTORY->ensureItemVisible( item );
		ListView_DIRECTORY->setSelected( item, TRUE );
		ListView_DIRECTORY->setCurrentItem( item );
		
		return TRUE;
	}
	else
	{
		return QWidget::event( event );
	}
}

/** */
void DCFileBrowser::slotThreadFinishedLoading( bool ok )
{
	if ( ok )
	{
		if ( m_pDecomp->recompressed != 0 )
		{
			m_ListData.SetSize(0);
			m_ListData.Append( m_pDecomp->recompressed->Data(), m_pDecomp->recompressed->Size() );
			delete m_pDecomp->recompressed;
		}
		ContinueInitTree( m_pDecomp->getData() ); // which will delete the data when it finishes
	}
	else
	{
		QMessageBox::critical(
			this,
			tr("Failed to load filelist"),
			tr("Unable to load") + " " + m_sFileName,
			tr("OK")
		);
		setCaption(tr("Filebrowser"));
		EnableGuiElements();
	}
	
	disconnect( m_pDecomp, SIGNAL(finished(bool)), this, SLOT(slotThreadFinishedLoading(bool)) );
	delete m_pDecomp;
	m_pDecomp = 0;
}

/** */
void DCFileBrowser::ClearView()
{
	ListView_FILES->clear();

	Q3ListViewItemIterator it( ListView_DIRECTORY );

	for ( ; it.current(); it++ )
	{
		((DCFileBrowserListItem*)it.current())->m_pFileList.clear();
	}

	ListView_DIRECTORY->clear();
}

/** */
void DCFileBrowser::slotFileOpen()
{
	QString s = QFileDialog::getOpenFileName(
		this,
		tr("Choose a file"),
		QString(g_pConfig->GetFileListPath().Data()),
		tr("Modern XML Filelists") + " (*.xml.bz2);;" +
		tr("Old BZ2 Text Filelists") + "(*.bz2);;" +
		tr("Very old HE3 Text Filelists") + "(*.DcLst);;" +
		tr("Old Valknut Filelists") + " (*.filelist);;" +
		tr("All files") + " (*.*)"
	);

	if ( s == "" )
	{
		return;
	}
	
	InitTree("","","",s);
}

/** */
void DCFileBrowser::slotFileSave()
{
	if ( m_ListData.Size() == 0 )
	{
		QMessageBox::critical(
			this,
			tr("Cannot save file"),
			tr("No data available to save."),
			tr("OK")
		);
		return;
	}
	
	QString s = QFileDialog::getSaveFileName(
		this,
		tr("Choose a filename to save under"),
		m_sFileName
	);

	if ( s == "" )
	{
		return;
	}
	
	if ( m_ListData.SaveToFile(s.toAscii().data()) == FALSE )
	{
		QMessageBox::critical(
			this,
			tr("Error saving file"),
			tr("Failed to save") + " " + s,
			tr("OK")
		);
	}
}

/** */
void DCFileBrowser::slotCurrentChangedDirectory()
{
	DCFileItem * FileItem;
	CDir dir;
	QString s;
	QList<Q3ListViewItem*> selitems;
	ulonglong size=0;
	int items = 0;
	int citems = 0;
	Q3ListViewItem * item = 0;

	// clear filelist
	ListView_FILES->clear();

	// check if only one dir selected
	if ( selectedItems( ListView_DIRECTORY, selitems ) == 1 )
	{
		item = selitems.first();

		for ( int i = 0; i < ((DCFileBrowserListItem*)item)->m_pFileList.size(); i++ )
		{
			FileItem = ((DCFileBrowserListItem*)item)->m_pFileList.at(i);
			
			if ( (g_pConfig->GetFoldersInRightPane() == FALSE) && (FileItem->m_bIsDir == TRUE) )
			{
				// do not show it
				continue;
			}
			
			size += FileItem->m_sSize.toULongLong();
			s = dir.Extension(FileItem->m_sName.toAscii().data()).Data();

			if ( s != "" )
				s += " " + tr("file");

			DCFBListViewItemWithPointer *item1 = new DCFBListViewItemWithPointer(ListView_FILES);
			item1->myvalue = FileItem->m_sSize.toULongLong();
			item1->mycol   = 1;
			item1->m_bSortTop = FALSE;
			item1->pDirItem = ((DCFileBrowserListItem*)item);

			if ( FileItem->m_bIsDir )
			{
				s = tr("Folder");
				item1->setPixmap(0,m_FolderPixmap);
				
				if ( g_pConfig->GetFoldersInRightPaneOnTop() == TRUE )
				{
					item1->m_bSortTop = TRUE;
				}
			}
			else
			{
				eFileTypes type = CFileManager::Instance()->GetFileType(FileItem->m_sName.toAscii().data());
				QPixmap icon;
				
				switch (type)
				{
					case eftMP3:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_MP3);
						break;
					case eftARCHIVE:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_ARCHIVE);
						break;
					case eftDOCUMENT:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_DOCUMENT);
						break;
					case eftAPPLICATION:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_APPLICATION);
						break;
					case eftPICTURE:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_PICTURE);
						break;
					case eftVIDEO:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_VIDEO);
						break;
					default:
						icon = g_pIconLoader->GetPixmap(eiFILETYPE_UNKNOWN);
						break;
				}
				
				item1->setPixmap( 0, icon );

				items++;
			}

			item1->setText(0,FileItem->m_sName);
			item1->setText(1,CUtils::GetSizeString(FileItem->m_sSize.toULongLong(), g_pConfig->GetUnit()).Data() );
			item1->setText(2,FileItem->m_sSize);
			item1->setText(3,s);
			item1->setText(4,FileItem->m_sHash);
		}
	}

	if ( item )
	{
		citems = item->childCount();
	}

	TextLabel_STATUS->setText( QString().setNum(items+citems) + " " + tr("Items") + " - " + 
					QString().setNum(items) + " " + tr("Files") +
					" (" + QString(CUtils::GetSizeString(size,g_pConfig->GetUnit()).Data()) + " " + tr("Total") + ") - " +
					QString().setNum(citems) + " " + tr("Directories") );
}

/** */
void DCFileBrowser::slotExpandedDirectory( Q3ListViewItem * item )
{
	if ( item->parent() != NULL )
		item->setPixmap(0,g_pIconLoader->GetPixmap(eiFOLDER_BLUE_OPEN));
}

/** */
void DCFileBrowser::slotCollapsedDirectory( Q3ListViewItem * item )
{
	if ( item->parent() != NULL )
		item->setPixmap(0,g_pIconLoader->GetPixmap(eiFOLDER_BLUE));
}

/** */
void DCFileBrowser::slotRightButtonClickedDirectory( Q3ListViewItem * item, const QPoint &, int column )
{
	CreateMenu(item,TRUE,column);
}

/** */
void DCFileBrowser::slotRightButtonClickedFiles( Q3ListViewItem * item, const QPoint &, int column )
{
	CreateMenu(item,FALSE,column);
}

/** */
void DCFileBrowser::AddDirectory( Q3ListViewItem * item, QString name )
{
	((DCFileBrowserListItem*)item)->m_sName = name;

	item->setText(0,name);
	item->setPixmap(0,m_FolderPixmap);
	
	((DCFileBrowserListItem*)item)->m_nBytes = 0;
}

/** */
void DCFileBrowser::AddFile( Q3ListViewItem * item, DCFileItem * fileitem )
{
	// increment filecounter
	if ( fileitem->m_bIsDir == FALSE )
		m_nFileCount++;

	// check size
	if ( fileitem->m_sSize == "" )
		fileitem->m_sSize = tr("<wrong length>");
	else
		if ( fileitem->m_bIsDir == FALSE )
			m_nShareSize += fileitem->m_sSize.toULongLong();

	// add fileitem to filelist
	((DCFileBrowserListItem*)item)->m_pFileList.append(fileitem);
	
	// add to folder contents size
	((DCFileBrowserListItem*)item)->m_nBytes += fileitem->m_sSize.toULongLong();
	
	DCFileItem * pFileItem = ((DCFileBrowserListItem*)item)->m_pFileItem;
	
	if ( pFileItem != 0 )
	{
		pFileItem->m_sSize = QString().setNum( ((DCFileBrowserListItem*)item)->m_nBytes );
	}
}

/** */
void DCFileBrowser::InitTree( QString nick, QString hubname, QString hubhost, QString filename )
{
	if ( filename == "" )
	{
		return;
	}
	
	if ( m_pDecomp != 0 )
	{
		QMessageBox::critical(
			this,
			tr("Cannot open list now"),
			tr("Filelist browser is busy"),
			tr("OK")
		);
		return;
	}
	
	// store values
	m_sNick     = nick;
	m_sHubName  = hubname;
	m_sHubHost  = hubhost;
	m_sFileName = filename;
	
	// first check if filename is our own list
	if ( filename == "/dev/null/ownfilelist" )
	{
		GetOwnList();
		
		//printf("Size of own list = %lu\n", m_ListData.Size());
		
		if ( (m_sFileName == "myfiles.xml.bz2") || (m_sFileName == "myfiles.txt.bz2") || (m_sFileName == "myfiles.DcLst") )
		{
			// nothing, it all got moved into the thread
		}
		else
		{
			QMessageBox::critical(
				this,
				tr("Error opening own filelist"),
				tr("Failed to open your own filelist"),
				tr("OK")
			);
			
			return;
		}
		
		DisableGuiElements();
		m_pDecomp = new DCFilelistDecompressor( this, &m_ListData );
		connect( m_pDecomp, SIGNAL(finished(bool)), this, SLOT(slotThreadFinishedLoading(bool)) );
		m_pDecomp->start();
		return;
	}
	else
	{
		QFile file(filename);
		if ( !file.exists() )
		{
			QMessageBox::critical(
				this,
				tr("Error opening filelist"),
				tr("File") + " " + filename + " " + tr("does not exist."),
				tr("OK")
			);
			
			return;
		}
		
		DisableGuiElements();
		m_pDecomp = new DCFilelistDecompressor( this, filename.toAscii().data() );
		connect( m_pDecomp, SIGNAL(finished(bool)), this, SLOT(slotThreadFinishedLoading(bool)) );
		m_pDecomp->start();
		return;
	}
}

/** */
void DCFileBrowser::ContinueInitTree( CString * data )
{
	Q3ListViewItem * item;
	
	// reset count values
	m_nShareSize = 0;
	m_nFileCount = 0;

	ClearView();

	// get folder pixmap
	m_FolderPixmap = g_pIconLoader->GetPixmap(eiFOLDER_BLUE);

	// disable updates
	ListView_DIRECTORY->setUpdatesEnabled(FALSE);
	
	if ( (data == 0) || (data->Length() == 0) )
	{
		QMessageBox::critical(
			this,
			tr("Error opening filelist"),
			m_sFileName + " " + tr("is likely not a valid filelist"),
			tr("OK")
		);
		return;
	}
	
	// parse filename
	
	QFileInfo fi(m_sFileName);
	QString fname = fi.fileName();
	
	if ( (m_sNick == "") || (m_sHubName == "") || (m_sHubHost == "") )
	{
		int i = fname.indexOf('@');
		if ( i != -1 )
		{
			m_sNick = fname.left(i);
			m_sHubHost = fname.mid(i+1);
			m_sHubHost.replace(".xml.bz2","");
			m_sHubHost.replace(".bz2","");
			m_sHubHost.replace(".DcLst","");
			m_sHubHost.replace(".filelist","");
			
			// replace _ with : before port number
			int i2 = m_sHubHost.lastIndexOf('_');
			if ( i2 != -1 )
			{
				bool ok;
				m_sHubHost.mid(i2+1).toInt(&ok);
				if ( ok == TRUE )
				{
					m_sHubHost = m_sHubHost.left(i2) + ":" + m_sHubHost.mid(i2+1);
				}
			}
			
			if ( m_sHubName == "" )
			{
				m_sHubName = m_sHubHost;
			}
		}
	}
	
	// set dialog caption
	
	if ( m_sNick == "" )
	{
		fname.replace(".xml.bz2","");
		fname.replace(".bz2","");
		fname.replace(".DcLst","");
		fname.replace(".filelist","");
		m_sNick = fname;
	}
	
	if ( m_sHubName != "" )
	{
		setCaption( m_sNick + " - " + tr("Filebrowser") + " [" + m_sHubName + "]" );
	}
	else
	{
		setCaption( m_sNick + " - " + tr("Filebrowser") );
	}
	
	// create root item
	item = new DCFileBrowserListItem( ListView_DIRECTORY );
	item->setText(0,tr("Root Directory"));
	item->setPixmap(0,g_pIconLoader->GetPixmap(eiFOLDER_RED));
	((DCFileBrowserListItem*)item)->m_pFileItem = 0;

	if ( data->Left(5) == "<?xml" )
		InitXMLTree(item,data);			
	else
		InitTXTTree(item,data);
	
	delete data;

	// update summary
	TextLabel_SUMMARY->setText( QString(CString().setNum(m_nFileCount).Data()) + " " + tr("Files") + " (" + 
					QString(CUtils::GetSizeString(m_nShareSize,g_pConfig->GetUnit()).Data()) + " " + tr("Total") + ")" );

	// update tooltip
	QToolTip::add( TextLabel_SUMMARY, (CString().setNum(m_nShareSize) + " B").Data() );	

	// update view
	ListView_DIRECTORY->setUpdatesEnabled(TRUE);
	ListView_DIRECTORY->triggerUpdate();

	// open first tree
	ListView_DIRECTORY->setOpen(ListView_DIRECTORY->firstChild(),TRUE);
	
	EnableGuiElements();
}

/** */
void DCFileBrowser::DisableGuiElements()
{
	setCaption(tr("Processing filelist..."));
	ToolButton_OPEN->setEnabled(FALSE);
	ToolButton_SAVE->setEnabled(FALSE);
	ListView_DIRECTORY->setEnabled(FALSE);
	ListView_FILES->setEnabled(FALSE);
}

void DCFileBrowser::EnableGuiElements()
{
	ToolButton_OPEN->setEnabled(TRUE);
	ToolButton_SAVE->setEnabled(TRUE);
	ListView_DIRECTORY->setEnabled(TRUE);
	ListView_FILES->setEnabled(TRUE);
}

/** */
void DCFileBrowser::InitTXTTree( Q3ListViewItem * item, CString * data )
{
	QString s1,sname,slength;
	long i,j,d;
	int depth;

	i = j = 0;

	while((i=data->Find("\xD\xA",j))>=0)
	{
		s1 = data->Mid(j,i-j).Data();

		depth=0;
		while( (s1.find("\x9",depth)) != -1 )
			depth++;
		s1.replace( "\x9", "" );

		if (item)
			while ( depth < item->depth() )
				if ( (item=item->parent()) == 0 )
					break;

		if ( s1 == "" )
			s1 = "\\";

		if ( s1 != "" )
		{
			d = s1.findRev("|");

			if ( d != -1 ) // parse file entry
			{
				DCFileItem * FileItem = new DCFileItem();

				FileItem->m_sName = s1.mid(0,d);
				FileItem->m_sSize = s1.right(s1.length()-d-1);
				FileItem->m_bIsDir = FALSE;
				
				AddFile( item, FileItem );
			}
			else // parse dir entry
			{
				DCFileItem * FileItem = new DCFileItem();
				
				FileItem->m_sName = s1;
				FileItem->m_sSize = "0";
				FileItem->m_bIsDir = TRUE;
				
				AddFile( item, FileItem );
				
				if ( item == 0 )
					item = new DCFileBrowserListItem( ListView_DIRECTORY );
				else
					item = new DCFileBrowserListItem( item );
				
				((DCFileBrowserListItem*)item)->m_pFileItem = FileItem;

				AddDirectory( item, s1 );
			}
		}

		j = i+2;
	}
	
	// fixup the folder sizes
	Q3ListViewItem * cur = ListView_DIRECTORY->firstChild();
	while ( cur )
	{
		CalcDirSize( cur );
		
		cur = cur->nextSibling();
	}
}

/** */
void DCFileBrowser::InitXMLTree( Q3ListViewItem * item, CString * data )
{
	CXml * xml;
	xmlNodePtr node;

	xml = new CXml();
	
	// strlen(data.Data()) replaces data.Length() to fix bug
	// with codec for C strings set to UTF-8
	if ( xml->ParseMemory(data->Data(),strlen(data->Data())) == TRUE )
	{
		for(node=xml->doc()->children;node!=0;node=node->next)
		{
			if ( !xmlStrcmp( node->name, (const xmlChar*)"FileListing" ) )
			{
				ParseXMLTree(xml,node->xmlChildrenNode,item);
			}
		}
	}

	delete xml;
}

/** */
void DCFileBrowser::ParseXMLTree( CXml * xml, xmlNodePtr node, Q3ListViewItem * item )
{
	CString s;

	for(;node!=0;node=node->next)
	{
		if ( !xmlStrcmp( node->name, (const xmlChar*)"Directory" ) )
		{
			Q3ListViewItem * item1;
			
			item1 = new DCFileBrowserListItem( item );
			((DCFileBrowserListItem*)item1)->m_pFileItem = 0;
			
			s = xml->prop(node,"Name");
			AddDirectory(item1,QString::fromUtf8(s.Data()));
			
			ParseXMLTree(xml,node->xmlChildrenNode,item1);
			
			DCFileItem * FileItem = new DCFileItem();
			FileItem->m_sName = QString::fromUtf8(s.Data());
			FileItem->m_sSize = QString().setNum(((DCFileBrowserListItem*)item1)->m_nBytes);
			FileItem->m_bIsDir = TRUE;
			AddFile(item,FileItem);
			((DCFileBrowserListItem*)item1)->m_pFileItem = FileItem;
			
			if ( g_pConfig->GetFolderSizesInLeftPane() == TRUE )
			{
				CString size = CUtils::GetSizeString( ((DCFileBrowserListItem*)item1)->m_nBytes, g_pConfig->GetUnit() );
				item1->setText( 1, size.Data() );
			}
		}
		else if ( !xmlStrcmp( node->name, (const xmlChar*)"File" ) )
		{
			DCFileItem * FileItem = new DCFileItem();
			s = xml->prop(node,"Name");
			FileItem->m_sName = QString::fromUtf8(s.Data());
			FileItem->m_sSize = xml->prop(node,"Size").Data();
			FileItem->m_sHash = xml->prop(node,"TTH").Data();
			FileItem->m_bIsDir = FALSE;
			
			AddFile(item,FileItem);
		}
	}
}

/** */
int DCFileBrowser::selectedItems( Q3ListView * list, QList<Q3ListViewItem*> & lst )
{
	Q3ListViewItemIterator it( (Q3ListView *)list );

	for ( ; it.current(); it++ )
	{
		if ( it.current()->isSelected() )
		{
			lst.append(it.current());
		}
	}

	return lst.count();
}

/** */
QString DCFileBrowser::CreateRemotePath( Q3ListViewItem * item )
{
	QString s;
	Q3ListViewItem * pitem;
	
	if( !item )
	{
		return "";
	}

	pitem = item;
	s = ((DCFileBrowserListItem*)pitem)->m_sName;
	
	while ( (pitem=pitem->parent()) != 0 )
	{
		// check for root entry
		if ( pitem->parent() != 0 )
		{
			s = ((DCFileBrowserListItem*)pitem)->m_sName + "\\" + s;
		}
	}

	s.replace( "\\\\\\\\", "\\" );

	return s;
}

/** */
void DCFileBrowser::CreateMenu( Q3ListViewItem *, bool direntry, int column )
{
	int i;
	QString s;
	ulonglong size;
	QString rootPath  = QString::null;
	QString localName = QString::null;
	QList<Q3ListViewItem*> selitems;
	Q3ListViewItem * curitem;
	bool hasFolderItems = FALSE;
	
	if ( direntry )
	{
		i = selectedItems( ListView_DIRECTORY, selitems );
	}
	else
	{
		i = selectedItems( ListView_FILES, selitems );
		
		QListIterator<Q3ListViewItem*> it ( selitems );
		
		Q3ListViewItem * cur = 0;
		
		while ( it.hasNext() )
		{
			cur = it.next();
			
			if ( cur->text(3) == tr("Folder") )
			{
				hasFolderItems = TRUE;
				break;
			} 
		}
	}

	// nothing on no selection
	if ( i == 0 )
	{
		return;
	}

/* TODO readadd test
	if ( direntry && (item->parent() == 0) )
	{
		return;
	}
*/
	QMenu * m = new QMenu(this);
	DCMenuHandler mhandler;

	QAction * dl = mhandler.addAction(m, emiDOWNLOAD);
	QAction * dl_to = mhandler.addAction(m, emiDOWNLOAD_TO);
	// disable 'download as' on directories
	QAction * dl_as = mhandler.addAction(m, emiDOWNLOAD_AS, ((direntry ? FALSE : TRUE) && !hasFolderItems && (i == 1) ));
	QAction * dl_in = mhandler.addAction(m, emiDOWNLOAD_IN, ((direntry ? FALSE : TRUE) && !hasFolderItems));
	mhandler.addAction(m, emiSEPARATOR);
	QAction * copycol = mhandler.addAction(m, emiCOPY_COLUMN_TO_CLIPBOARD, (direntry ? FALSE : TRUE));
	QAction * copyrow = mhandler.addAction(m, emiCOPY_ROW_TO_CLIPBOARD, (direntry ? FALSE : TRUE));
	QAction * copymagnet = mhandler.addAction(m, emiCOPYMAGNETLINK, ((direntry ? FALSE : TRUE) && !hasFolderItems ));
	
	QAction * chosen = m->exec(QCursor::pos());

	delete m;
	
	if ( chosen == copycol )
	{
		QString s="";

                for ( int c = 0; c < selitems.size(); c++ )
                {
			curitem = selitems.at(c);
			s += curitem->text(column) + "\n";
		}

		QApplication::clipboard()->setText(s);
	}
	else if ( chosen == copyrow )
	{
		int idx;
		QString s="";

                for ( int c = 0; c < selitems.size(); c++ )
                {
			curitem = selitems.at(c);
			
			for( idx = 0; idx < ListView_FILES->columns(); idx++ )
			{
				s += curitem->text(idx) + " ";
			}

			s += "\n";
		}

		QApplication::clipboard()->setText(s);
	}
	else if ( chosen == copymagnet )
	{
		//magnet:?xt=urn:tree:tiger:EOSA5AGTL5SD3VWCF3R2OH2WMGXV3S3R7SYN4YA&xl=708780032&dn=FC-6-i386-disc1.iso
		QString all = "";
		
		for ( int c = 0; c < selitems.size(); c++ )
		{
			curitem = selitems.at(c);
			
			// add tth
			all += "magnet:?xt=urn:tree:tiger:";
			all += curitem->text(4);
			
			// add filesize
			all += "&xl=";
			all += curitem->text(2);
			
			// add filename
			all += "&dn=";
			
			QString filename = QString(QUrl::toPercentEncoding(curitem->text(0)));
			
			//fixme DC++ replaces spaces with + character
			//filename = filename.replace(' ','+');
			
			filename.replace("%20","+");
			all += filename;
			
			all += "\n";
		}
		
		// remove trailing "\n"
		all = all.stripWhiteSpace();
		
		QApplication::clipboard()->setText(all);
	}
	// Check if the user wants a non-default destination
	else if ( chosen == dl_to )
	{
		rootPath = QFileDialog::getExistingDirectory( this, tr("Select destination") );
		if ( rootPath.isEmpty() )
		{
			// If the user cancel, then we don't download
			return;
		}
	}
	// Should the file be renamed
	else if ( chosen == dl_as )
	{
		rootPath = QFileDialog::getSaveFileName( this, tr("Select a filename"), selitems.first()->text(0) );
		if ( rootPath.isEmpty() )
		{
			return;
		}

		QFileInfo fi(rootPath);
		rootPath = fi.dirPath();
		localName = fi.fileName();

		if ( rootPath.isEmpty() || localName.isEmpty() )
		{
			return;
		}
	}
	else if ( chosen == dl_in )
	{
		QString localrootpath;
		QString localname;
		QString localpath;
		QString remotePath;

		if ( !(curitem = selitems.first()) )
			return;

		size = ((DC_QListViewItem *)curitem)->myvalue;

		// all files need equal size
                for ( int c = 0; c < selitems.size(); c++ )
                {
			curitem = selitems.at(c);
			
			if ( size != ((DC_QListViewItem *)curitem)->myvalue )
				return;
		}

		if ( m_FileTool.SelectFileSource( size, localname, localrootpath, localpath ) == FALSE )
		{
			return;
		}

		// get the full remote path
		remotePath = CreateRemotePath(GetDirItem(curitem));
		QString remotePathAndFile;
                for ( int c = 0; c < selitems.size(); c++ )
                {
			curitem = selitems.at(c);
			
			s = curitem->text(0);
			
			remotePathAndFile = remotePath + "\\" + s;
					    
			// add transfer to the waitlist
			m_FileTool.CheckFile( m_sNick.toAscii().data(), m_sHubName.toAscii().data(), m_sHubHost.toAscii().data(),
				  remotePathAndFile.toAscii().data(), localname.toAscii().data(), localpath.toAscii().data(), localrootpath.toAscii().data(), eltFILE,
		  		  size, curitem->text(4).toAscii().data(), TRUE);
		}
	}

	// Is this a download?
	if ( (chosen == dl) || (chosen == dl_to) || (chosen == dl_as) )
	{
                for ( int c = 0; c < selitems.size(); c++ )
                {
			curitem = selitems.at(c);
			
			// directory download from left pane
			if ( direntry )
			{
				s = curitem->text(0);
					
				DownloadPath( rootPath, s, QString::null, curitem );
			}
			else if ( curitem->text(3) == tr("Folder") ) // directory download from right pane
			{
				Q3ListViewItem * diritem = GetDirItem( curitem );
				
				if ( diritem == 0 )
				{
					QMessageBox::critical( this, tr("Cannot download"), tr("Could not find the path for ") + curitem->text(0) );
				}
				else
				{
					DownloadPath( rootPath, diritem->text(0), QString::null, diritem );
				}
 			}
			// Single file download
			else
			{
				QString remotePath;
				QString remoteName;
				
				// set localname
				if ( chosen != dl_as )
				{
					localName = curitem->text(0);
				}

				// get the full remote path
				remotePath = CreateRemotePath(GetDirItem(curitem));
				
				remoteName = curitem->text(0);
				
				DownloadFile( rootPath, "", localName, remotePath, remoteName, ((DC_QListViewItem *)curitem)->myvalue, curitem->text(4) );
			}
		}
	}
}

/** */
void DCFileBrowser::DownloadPath( QString rootPath, QString localPath, QString localName, Q3ListViewItem * item )
{
	QString remotePath;
	DCFileItem * FileItem = 0;
	QString s;
	QString remoteFile;

	if ( !item )
	{
		return;
	}
	
	// get the full remote path
	remotePath = CreateRemotePath(item);
	
	for ( int i = 0; i < ((DCFileBrowserListItem*)item)->m_pFileList.size(); i++ )
	{
		FileItem = ((DCFileBrowserListItem*)item)->m_pFileList.at(i);
		
		if ( FileItem->m_bIsDir == FALSE )
		{
			remoteFile = FileItem->m_sName;
			DownloadFile( rootPath, localPath, localName, remotePath, 
				remoteFile, FileItem->m_sSize.toULongLong(), FileItem->m_sHash );
		}
	}

	item = item->firstChild();

	while( item )
	{
		s = item->text(0);
			
		DownloadPath(rootPath,localPath + "\\" + s,localName,item);
		item = item->nextSibling();
	}
}

/** */
void DCFileBrowser::DownloadFile( QString rootPath, QString localPath, QString localName, 
				  QString remotePath, QString remoteName, ulonglong size,
				  QString hash )
{
	// append filename to the remote path
	remotePath += "\\" + remoteName;
		    
	if ( localName == QString::null )
	{
		localName = remoteName;
	}

	
	m_FileTool.CheckFile(m_sNick.toAscii().data(),
		m_sHubName.toAscii().data(),
		m_sHubHost.toAscii().data(),
		remotePath.toAscii().data(),
		(const char*)localName.toAscii().data(),
		(const char*)localPath.toAscii().data(),
		(const char*)rootPath.toAscii().data(), eltFILE,
		size, hash.toAscii().data() );
}

/** Double click to download the item, copied from DCFileBrowser::CreateMenu */
void DCFileBrowser::slotItemDoubleClicked( Q3ListViewItem * item, const QPoint &, int )
{
	QString rootPath = QString::null;
	QString localName = QString::null;
	
	if ( item == 0 ) // null pointer check, just in case
	{
		return;
	}
	
	Q3ListViewItem * diritem = GetDirItem( item );
	
	if ( diritem == 0 )
	{
		QMessageBox::critical( this, tr("Cannot download"), tr("Could not find the path for ") + item->text(0) );
		return;
	}
	
	if ( item->text(3) == tr("Folder") )
	{
		// we cannot open the folder since that would delete the existing
		// Q3ListViewItems, which is not allowed in this slot and causes
		// random crashes
		
		/* Q3ListViewItem * diritem = ListView_DIRECTORY->currentItem();
		
		if ( diritem == 0 )
		{
			printf("Double click a folder needs a ListView_DIRECTORY->currentItem()\n");
			return;
		}
		
		for ( diritem = diritem->firstChild(); diritem != 0; diritem = diritem->nextSibling() )
		{
			if ( diritem->text(0) == item->text(0) )
			{
				ListView_DIRECTORY->clearSelection();
				ListView_DIRECTORY->ensureItemVisible( diritem );
				ListView_DIRECTORY->setSelected( diritem, TRUE );
				ListView_DIRECTORY->setCurrentItem( diritem );
				break;
			}
		} */
		
		if ( g_pConfig->GetOpenFoldersInRightPane() == TRUE )
		{
			QApplication::postEvent( this, new DC_OpenFilelistFolderEvent( EVENT_OPEN_FILELIST_FOLDER, (DCFileBrowserListItem*)diritem ) );
		}
		else
		{
			// ask for confirmation since this is unusual behaviour
			int confirm = QMessageBox::question(
				this,
				tr("Download contents?"),
				tr("Download the contents of \"") + diritem->text(0) + "\" ?",
				QMessageBox::Yes,
				QMessageBox::No
			);
			
			if ( confirm == QMessageBox::Yes )
			{
				DownloadPath( rootPath, diritem->text(0), localName, diritem );
			}
		}
	}
	else
	{
		QString remotePath;
		QString remoteName;
		
		// get the full remote path
		remotePath = CreateRemotePath(diritem);
		
		remoteName = item->text(0);
		
		DownloadFile( rootPath, "", localName, remotePath, remoteName, ((DC_QListViewItem *)item)->myvalue, item->text(4) );
	}
}

/** Only needed for non-xml filelists */
ulonglong DCFileBrowser::CalcDirSize( Q3ListViewItem * item )
{
	ulonglong total = 0;

	DCFileBrowserListItem * cur = (DCFileBrowserListItem*) item;
	
	QListIterator<DCFileItem*> it( cur->m_pFileList );
	
	DCFileItem * current = 0;
	
	while ( it.hasNext())
	{
		current = it.next();
		
		if ( current->m_bIsDir == TRUE )
		{
			Q3ListViewItem * child = item->firstChild();
			
			while ( child )
			{
				if ( child->text(0) == current->m_sName )
				{
					total += CalcDirSize( child );
					break;
				}
				
				child = child->nextSibling();
			}
		}
		else
		{
			total += current->m_sSize.toULongLong();
		}
	}
	
	DCFileItem * pFileItem = cur->m_pFileItem;
	if ( pFileItem != 0 )
	{
		pFileItem->m_sSize = QString().setNum(total);
	}
	
	cur->m_nBytes = total;
	
	if ( g_pConfig->GetFolderSizesInLeftPane() == TRUE )
	{
		CString size = CUtils::GetSizeString( total, g_pConfig->GetUnit() );
		cur->setText( 1, size.Data() );
	}
	
	//printf( "CalcDirSize for %s = %lld\n", item->text(0).toAscii().data(), total );
	
	return total;
}

/** */
Q3ListViewItem * DCFileBrowser::GetDirItem( Q3ListViewItem * item )
{
	Q3ListViewItem * diritem = ((DCFBListViewItemWithPointer*)item)->pDirItem;
	
	if ( item == 0 )
	{
		return 0;
	}

	if ( item->text(3) != tr("Folder") )
	{
		return diritem;
	}
	
	if ( diritem == 0 )
	{
		return 0;
	}
	
	for ( diritem = diritem->firstChild(); diritem != 0; diritem = diritem->nextSibling() )
	{
		if ( diritem->text(0) == item->text(0) )
		{
			return diritem;
		}
	}
	
	return 0;
}

/** */
void DCFileBrowser::GetOwnList()
{
	m_ListData.SetSize(0);
	
	if ( CFileManager::Instance()->GetShareBuffer( esbtXMLBZ, &m_ListData, FALSE ) == 0 )
	{
		m_sFileName = "myfiles.xml.bz2";
	}
	else if ( CFileManager::Instance()->GetShareBuffer( esbtBZ, &m_ListData, FALSE ) == 0 )
	{
		m_sFileName = "myfiles.txt.bz2";
	}
	else if ( CFileManager::Instance()->GetShareBuffer( esbtHE3, &m_ListData, FALSE ) == 0 )
	{
		m_sFileName = "myfiles.DcLst";
	}
	else
	{
		m_ListData.SetSize(0);
	}
}

/** */
DCFilelistDecompressor::DCFilelistDecompressor( QWidget * caller, CString file ) : QThread()
{
	gui = caller;
	filename = file;
	data = 0;
	compressed = 0;
	recompressed = 0;
}

/** */
DCFilelistDecompressor::DCFilelistDecompressor( QWidget * caller, CByteArray * memory ) : QThread()
{
	gui = caller;
	filename = "";
	data = 0;
	compressed = memory;
	recompressed = 0;
}

/** */
DCFilelistDecompressor::~DCFilelistDecompressor()
{
	/* It is DCFileBrowsers's responsibility to delete things */
	if ( isRunning() )
	{
		printf("~DCFilelistDecompressor: waiting for thread to finish\n");
		wait();
	}
}

/** */
CString * DCFilelistDecompressor::getData()
{
	return data;
}

/** */
void DCFilelistDecompressor::run()
{
	bool ok = FALSE;
	CByteArray out;
	
	if ( filename != "" )
	{
		compressed = new CByteArray();
		if ( compressed->LoadFromFile(filename) == FALSE )
		{
			emit finished( FALSE );
			return;
		}
	}
	
	CString test;
	test.Set( (const char*)compressed->Data(), 2 );
	if ( test == "BZ" )
	{
		CBZ * bz = new CBZ();
		
		if ( bz->Decompress( compressed, &out ) == TRUE )
		{
			ok = TRUE;
		}
		
		delete bz;
	}
	else
	{
		test.Set( (const char*)compressed->Data(), 3 );
		if ( test == "HE3" )
		{
			CHE3 * he3 = new CHE3();
			
			CString * s = he3->decode_he3_data( compressed );
			
			if ( s != 0 )
			{
				out.Append( s->Data(), s->Length() );
				delete s;
				ok = TRUE;
			}
			
			delete he3;
		}
		else
		{
			test.Set( (const char*)compressed->Data(), 18 );
			if ( test == "---HEADER START---" )
			{
				CString * s = LoadOldValknutList();
				if ( s != 0 )
				{
					out.Append( s->Data(), s->Length() );
					delete s;
					ok = TRUE;
					
					recompressed = new CByteArray();
					
					CBZ * bz = new CBZ();
					bz->Compress(&out,recompressed);
					delete bz;
				}
			}
		}
	}
	
	if ( ok == FALSE )
	{
		emit finished( FALSE );
		return;
	}
	
	data = new CString();
	
	test.Set( (const char*)out.Data(), 5 );
	
	if ( test == "<?xml" )
	{
		data->Set( (const char*)out.Data(), out.Size() );
	}
	else
	{
		CIconv * ciconv = new CIconv( g_pConfig->GetRemoteEncoding(), g_pConfig->GetLocalEncoding() );
		
		CString rawtext;
		rawtext.Set( (const char*)out.Data(), out.Size() );
		*data = ciconv->encode( rawtext );
		
		delete ciconv;
	}
	
	emit finished( ok );
}

/** */
CString * DCFilelistDecompressor::LoadOldValknutList()
{
	CString * data = new CString();
	
	QFile file( filename.Data() );

	if ( file.open( IO_ReadOnly ) )
	{
		QString line;

		line = QString::fromUtf8(file.readLine(255));
		
		if ( line == "---HEADER START---\n" )
		{
			// read header
			while ( line!="" && line!="---HEADER END---\n" )
			{
				line = QString::fromUtf8(file.readLine(255));

				if ( line.find("TYPE=") == 0 )
				{
					//m_sType = line.mid(5).simplified();
					//if ( m_sType == "XMLFILELIST" )
					//{
						//m_sType = "xml";
					//}
					//else
					//{
						//m_sType = "text";
					//}
				}
				else if ( line.find("NICK=") == 0 )
				{
					//m_sNick = line.mid(5).simplified();
				}
				else if ( line.find("HUBNAME=") == 0 )
				{
					//m_sHubName = line.mid(8).simplified();
				}
				else if ( line.find("HUBHOST=") == 0 )
				{
					//m_sHubHost = line.mid(8).simplified();
				}
			}

			// the loop reading one byte at a time was replaced
			// with this
			QByteArray ba = file.readAll();
			
			// WARNING! ba.data() includes trailing garbage!
			// it must only be used in conjunction with ba.size()
			
			data->Set( ba.data(), ba.size() );
		}

        	file.close();
	}
	
	//printf("LoadOldValknutFilelist output length=%d\n",data.Length());
	
	return data;
}
