/***************************************************************************
                          dcfiletool.cpp  -  description
                             -------------------
    begin                : Die Mär 26 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 <stdlib.h>

#include <qstring.h>
#include <qfile.h>
#include <qmessagebox.h>
#include <qinputdialog.h>
#include <qdir.h>
#include <qregexp.h>
#include <qpopupmenu.h>
#include <qcursor.h>

#include <dclib/core/cdir.h>
#include <dclib/chttp.h>

#include <dcconfig.h>
#include <dctransferview.h>
#include <dcconnectionmanager.h>

#include "dcfiletool.h"

#include <dclib/dcos.h>

/** */
DCFileTool::DCFileTool()
{
	Init();
}

/** */
DCFileTool::~DCFileTool()
{
}

/** */
void DCFileTool::Init()
{
	m_nLastDownloadCase = -1;
	m_bCaseAllFiles     = FALSE;
}

/** */
bool DCFileTool::SelectFileSource( ulonglong size, QString & file, QString & localrootpath, QString & localpath )
{
	bool res = FALSE;
	QStringVector LocalFilesList;
	int id;

	g_pTransferView->GetLocalFilesList( LocalFilesList, size );

	if ( LocalFilesList.empty() )
	{
		return res;
	}

	// Add as an extra source
	QPopupMenu * m = new QPopupMenu;

	for ( int iter=0; iter<(int)LocalFilesList.size(); iter++ )
	{
		if ( LocalFilesList[iter].contains("\\") )
		{
			m->insertItem( LocalFilesList[iter].section("\\",-1,-1), iter );
		}
		else
		{
			m->insertItem( LocalFilesList[iter].section("/",-1,-1), iter );
		}
	}

	m->insertItem( tr("(choose an existing download to add to)"), 999);
	m->setItemEnabled(999,false);

	id = m->exec(QCursor::pos());

	delete m;

	if ( id != -1 )
	{
		QFileInfo fi(LocalFilesList[id]);

		file          = fi.fileName();
		localpath     = "";
		localrootpath = fi.dirPath();

		// now we must check if a localpath exist
		if ( localrootpath.find( g_pConfig->GetDownloadFolder().Data(), 0 ) == 0 )
		{
			localpath     = localrootpath.mid( g_pConfig->GetDownloadFolder().Length(), localrootpath.length()-g_pConfig->GetDownloadFolder().Length() );
			localrootpath = g_pConfig->GetDownloadFolder().Data();
		}

		res = TRUE;
	}

	return res;
}

/** */
bool DCFileTool::AddFileSource( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium medium, ulonglong size, CString tth )
{
	bool res = FALSE;
	QString sourcefile = "",slocalrootpath,slocalpath;

	if ( SelectFileSource( size, sourcefile, slocalrootpath, slocalpath ) == FALSE )
	{
		return CheckFile( nick, hubname, hubhost,
				remotename, localname,
				localpath, localrootpath,
				medium, size, tth );
	}

	if ( !sourcefile.isEmpty() )
	{
		// add transfer to the waitlist
		res = CheckFile( nick, hubname, hubhost,
				remotename, sourcefile.ascii(),
				slocalpath.ascii(), slocalrootpath.ascii(),
				medium, size, tth, TRUE );

	}

	return res;
}

/** */
bool DCFileTool::CheckFile( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium medium, ulonglong size, CString hash,
				bool usermulti, eRepairType repair )
{
	CDir dir;
	CString s;
	QString name;
	ulonglong fsize, pos_s, pos_e;
	int filestate,i;
	bool multi = usermulti;

	// first we get the filestate from the downloadmanager
	filestate = g_pTransferView->DLM_QueueCheck( nick, hubname, hubhost,
			remotename, localname,
			localpath, localrootpath,
			medium, size );

	if ( filestate == 1 )
	{
//		QMessageBox::information( this, tr("File download"),
//			tr("The same user/file is already in the queue !\n"));
		return FALSE;
	}
	else if ( (filestate == 2) && (multi == FALSE) )
	{
		switch( QMessageBox::warning( this, tr("File download"),
			tr("A same file is already in the queue!"),
		        tr("Start a multi-download"),
			tr("Cancel"), 0, 1 ) )
		{
			case 0:
				multi = TRUE;
		        	break;
			default:
				return FALSE;
				break;
		}
	}
	else if ( filestate == 3 )
	{
		QMessageBox::information( this, tr("File download"),
			tr("File is already in the queue but not mark as a multi-download!\n"));
		return FALSE;
	}
	else if ( filestate == 4 )
	{
		QMessageBox::information( this, tr("File download"),
			tr("File is already in the queue with a different size!\n"));
		return FALSE;
	}
	else if ( filestate == 0 )
	{
		if ( localrootpath != "" )
		{
			name = localrootpath.Data();
		}
		else
		{
			name = g_pConfig->GetDownloadFolder().Data();
		}

		if ( name == "" )
		{
			return FALSE;
		}

		localpath = localpath.Replace(':',"");
		localname = localname.Replace(':',"");

		name = name + "/" + localpath.Data() + "/" + localname.Data();
		name = dir.SimplePath(name.ascii()).Data();

		QFile f(name);

		fsize = dir.getFileSize(name.ascii(),FALSE);

		// check already download
		if ( (size == fsize) && (fsize > 0) && !repair )
		{
			QMessageBox::information( this, tr("File download"),
				tr("File already downloaded!\n"));
			return FALSE;
		}

		// check for partial download
		if ( fsize > 0 )
		{
			if( repair )
				{
				// get desired byte range
				QString buf;
				char *b, *p;
				bool ok = FALSE;

				// There is probably a better way to get this info.  We assume 2352 sector size (ss)
				// which is most common for BIN/CUE, but we might want to make two menu entries, one
				// for 2352 and one for 2048 (ie. ISO).  The sector offset (so) is the number of bytes
				// into the BIN file where sector zero is reported.  Fireburner for Linux reports
				// from the beginning of the file, but other programs (like cdmage) report from the
				// first data sector (skipping all header and directory information).  We assume zero.
				int ss = 2352;
				int so = 0;

				if( repair == ertBYTES )
					buf = QInputDialog::getText(
						tr("Repair File"),
						tr("Enter byte range (m-n)"),
						QLineEdit::Normal, QString::null, &ok, 0 );
				else
					buf = QInputDialog::getText(
						tr("Repair BIN Sectors"),
						tr("Enter sector range (m[-n])"),
						QLineEdit::Normal, QString::null, &ok, 0 );

				if( !ok )
					return FALSE;

				b = (char*) buf.ascii();
				s = b;
				pos_s = s.asULL();

				p = strchr( b, '-' );
				if( p ) {
					s = p+1;
					pos_e = s.asULL();
				}
				else
					{
					if( repair == ertBYTES )
						return FALSE;
					pos_e = 0;
					}

				if( repair == ertSECTORS )
					{
					if( !pos_e )
						pos_e = pos_s;
					pos_s = so + pos_s * ss;
					pos_e = so + (pos_e+1) * ss - 1;
					}

				if( pos_e > fsize )
					pos_e = fsize;
				if( pos_s > pos_e )
					pos_s = pos_e;

				multi = TRUE;
				printf( "Starting download repair, %s, %lld-%lld\n", name.ascii(), pos_s, pos_e );
				}
			else
			{
				pos_e = 0;
				switch( QMessageBox::warning( this, tr("File download"),
					tr("File already exists!"),
					tr("Resume"),
					tr("Overwrite"),
					tr("Cancel"), 0) )
				{
					case 0:
						pos_s = fsize;
						break;
					case 1:
						f.remove();
						fsize = 0;
						pos_s = 0;
						break;
					case 2:
						return FALSE;
						break;
				}
			}
		}

		if ( fsize == 0 )
		{
			pos_s = 0;
			pos_e = 0;

			if ( size > (1024*1024) )
			{
				if ( (g_pConfig->GetDefaultDownloadMode() == 0) && (multi == FALSE) )
				{
					if ( m_bCaseAllFiles == FALSE )
					{
						i = QMessageBox::warning( this, tr("File download"),
									  tr("You can start a multi-download!"),
									  QMessageBox::Yes | QMessageBox::Default,
									  QMessageBox::No | QMessageBox::Escape,
									  0 );

						if ( m_nLastDownloadCase == -1 )
						{
							switch ( QMessageBox::warning( this, tr("File download"),
										       tr("Do you want to download all files in this mode?"),
										       QMessageBox::Yes | QMessageBox::Default,
										       QMessageBox::No | QMessageBox::Escape,
										       0 ) )
							{
							case QMessageBox::Yes:
								m_bCaseAllFiles = TRUE;
						        	break;
							default:
								break;
							}
						}

						m_nLastDownloadCase = i;
					}

					switch( m_nLastDownloadCase )
					{
						case QMessageBox::Yes:
							multi = TRUE;
				       		 	break;
						default:
							break;
					}
				}
				else if ( (g_pConfig->GetDefaultDownloadMode() == 1) && (multi == FALSE) )
				{
					multi=FALSE;
				}
				else
				{
					multi=TRUE;
				}
			}
			else
			{
				multi = FALSE;
			}
		}
	}

	if ( g_pConnectionManager->IsHubOnline(hubname,hubhost) == ehsNONE )
	{
		/* Check if the user is connected to another hub that we are connected to.
		 * I'm uncertain how it will react when multi==FALSE (thus that is not handled),
		 * but as this concerns small files it is a bit annoying.
		 */
		bool foundUser = FALSE;

		if ( multi == TRUE )
		{
			CList<DCHubObject> HubList;
			CString emptyString = CString("");
			DCHubObject * CurrentHub = 0;
			
			/* Get hublist for the user, ie hubs that both us and them are
			 * connected to.
			 */
			if( g_pConnectionManager->IsUserOnline(nick,emptyString,emptyString,&HubList) )
			{
				/* Add an item in the queue for each hub, mark the user as found */
				while((CurrentHub =  HubList.Next(CurrentHub) ) != 0 )
				{
					foundUser = TRUE;
					g_pTransferView->DLM_QueueAdd( nick, CurrentHub->m_sHubName, CurrentHub->m_sHubHost,
								remotename, localname,
								localpath, localrootpath,
								medium, size, pos_s, pos_e, hash, multi );
				}
			}
		}
		/* Usual behavior:
		 * (no user found or option not enabled)
		 */
		if(foundUser == FALSE)
		{
			switch( QMessageBox::warning( this, tr("File download"),
						tr("Not connected to required hub!"),
						tr("Connect"),
						tr("Cancel"), 0, 0, 1 ) )
			{
				case 0:
					g_pConnectionManager->Connect(hubname.Data(),hubhost.Data());
					break;
				case 1:
					break;
			}
		}
	}

	g_pTransferView->DLM_QueueAdd( nick, hubname, hubhost,
				remotename, localname,
				localpath, localrootpath,
				medium, size, pos_s, pos_e, hash, multi );

	return TRUE;
}
