/****************************************************************
 *  RetroShare is distributed under the following license:
 *
 *  Copyright (C) 2008 Robert Fernie
 *
 *  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., 51 Franklin Street, Fifth Floor, 
 *  Boston, MA  02110-1301, USA.
 ****************************************************************/

#include <QTimer>
#include <QMessageBox>
#include <QFileDialog>
#include <QUrl>

#include "SubFileItem.h"

#include <gui/common/RsUrlHandler.h>
#include <retroshare/rsfiles.h>
#include "util/misc.h"
#include "gui/RetroShareLink.h"

#include <retroshare/rschannels.h>

/****
 * #define DEBUG_ITEM 1
 ****/

/*******************************************************************
 * SubFileItem fully controls the file transfer from the gui side
 *
 * Display: (In order)
 *
 * 1) HASHING
 * 2) REMOTE / DOWNLOAD
 * 3) LOCAL
 * 4) LOCAL / UPLOAD
 *
 * Behaviours:
 * a) Addition to General Dialog (1), (2), (3)
 *   (i) (1), request Hash -> (3).
 *   (ii) (2), download complete -> (3)
 *   (iii) (3) 
 *
 * b) Message/Blog/Channel (2), (3)
 *   (i) (2), download complete -> (3)
 *   (ii) (3)
 *
 * c) Transfers (2), (4)
 *   (i) (2)
 *   (ii) (3)
 *
 *
 */



const uint32_t SFI_DEFAULT_PERIOD 	= (30 * 3600 * 24); /* 30 Days */

/** Constructor */
SubFileItem::SubFileItem(const std::string &hash, const std::string &name, const std::string &path, uint64_t size, uint32_t flags, const std::string &srcId)
:QWidget(NULL), mPath(path), mFileHash(hash), mFileName(name), mFileSize(size), mSrcId(srcId)
{
  	/* Invoke the Qt Designer generated object setup routine */
  	setupUi(this);

	setAttribute ( Qt::WA_DeleteOnClose, true );

	mMode = flags & SFI_MASK_STATE;
	mType = flags & SFI_MASK_TYPE;
	mFlag = flags & SFI_MASK_FLAG;

	/**** Enable ****
	*****/

	/* all other states are possible */

	if (!rsFiles) 
	{
		mMode = SFI_STATE_ERROR;
	}

	Setup();
}


void SubFileItem::Setup()
{
  connect( playButton, SIGNAL( clicked( void ) ), this, SLOT( play ( void ) ) );
  connect( downloadButton, SIGNAL( clicked( void ) ), this, SLOT( download ( void ) ) );
  connect( cancelButton, SIGNAL( clicked( void ) ), this, SLOT( cancel ( void ) ) );
  connect( copyLinkButton, SIGNAL( clicked( void ) ), this, SLOT( copyLink ( void ) ) );
  connect( saveButton, SIGNAL( clicked( void ) ), this, SLOT( save ( void ) ) );

  /* once off check - if remote, check if we have it 
   * NB: This check might be expensive - and it'll happen often!
   */
#ifdef DEBUG_ITEM
  std::cerr << "SubFileItem::Setup(): " << mFileName;
  std::cerr << std::endl;
#endif

  if (mMode == SFI_STATE_REMOTE)
  {
	FileInfo fi;

	/* look up path */
	if (rsFiles->alreadyHaveFile(mFileHash, fi))
	{
#ifdef DEBUG_ITEM
		std::cerr << "SubFileItem::Setup() STATE=>Local Found File";
		std::cerr << std::endl;
		std::cerr << "SubFileItem::Setup() path: " << fi.path;
		std::cerr << std::endl;
#endif
		mMode = SFI_STATE_LOCAL;
		mPath = fi.path;
	}
  }

  smaller();
  updateItemStatic();
  updateItem();
}


bool SubFileItem::done()
{
	return (mMode >= SFI_STATE_LOCAL);
}

bool SubFileItem::ready()
{
	return (mMode >= SFI_STATE_REMOTE);
}

void SubFileItem::updateItemStatic()
{
	/* fill in */
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::updateItemStatic(): " << mFileName;
	std::cerr << std::endl;
#endif

	QString filename = QString::fromUtf8(mFileName.c_str());
	mDivisor = 1;

	if (mFileSize > 10000000) /* 10 Mb */
	{
		progressBar->setRange(0, mFileSize / 1000000);
		progressBar->setFormat("%v MB");
		mDivisor = 1000000;
	}
	else if (mFileSize > 10000) /* 10 Kb */
	{
		progressBar->setRange(0, mFileSize / 1000);
		progressBar->setFormat("%v kB");
		mDivisor = 1000;
	}
	else 
	{
		progressBar->setRange(0, mFileSize);
		progressBar->setFormat("%v B");
		mDivisor = 1;
	}

	/* get full path for local file */
	if ((mMode == SFI_STATE_LOCAL) || (mMode == SFI_STATE_UPLOAD))
	{
#ifdef DEBUG_ITEM
		std::cerr << "SubFileItem::updateItemStatic() STATE=Local/Upload checking path";
		std::cerr << std::endl;
#endif
		if (mPath == "")
		{
			FileInfo fi;
			FileSearchFlags hintflags = RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_SPEC_ONLY | RS_FILE_HINTS_NETWORK_WIDE | RS_FILE_HINTS_BROWSABLE;

			/* look up path */
			if (!rsFiles->FileDetails(mFileHash, hintflags, fi))
			{
				mMode = SFI_STATE_ERROR;
#ifdef DEBUG_ITEM
			std::cerr << "SubFileItem::updateItemStatic() STATE=>Error No Details";
			std::cerr << std::endl;
#endif
			}
			else
			{
#ifdef DEBUG_ITEM
			std::cerr << "SubFileItem::updateItemStatic() Updated Path";
			std::cerr << std::endl;
#endif
				mPath = fi.path;
			}
		}
	}

	/* do buttons + display */
	switch (mMode)
	{
		case SFI_STATE_ERROR:
			progressBar->setRange(0, 100);
			progressBar->setFormat(tr("ERROR"));

			playButton->setEnabled(false);
			downloadButton->setEnabled(false);
			cancelButton->setEnabled(false);

			progressBar->setValue(0);
			filename = "[" + tr("ERROR") + "] " + filename;

			break;

		case SFI_STATE_EXTRA:
			filename = QString::fromUtf8(mPath.c_str());

			progressBar->setRange(0, 100);
			progressBar->setFormat("HASHING");

			playButton->setEnabled(false);
			downloadButton->setEnabled(false);
			cancelButton->setEnabled(false);

			progressBar->setValue(0);
			filename = "[" + tr("EXTRA") + "] " + filename;

			break;

		case SFI_STATE_REMOTE:
			playButton->setEnabled(false);
			downloadButton->setEnabled(true);
			cancelButton->setEnabled(false);

			progressBar->setValue(0);
			filename = "[" + tr("REMOTE") + "] " + filename + " (" + misc::friendlyUnit(mFileSize) + ")";

			break;

		case SFI_STATE_DOWNLOAD:
			playButton->setEnabled(false);
			downloadButton->setEnabled(false);
			cancelButton->setEnabled(true);
			filename = "[" + tr("DOWNLOAD") + "] " + filename;

			break;

		case SFI_STATE_LOCAL:
			playButton->setEnabled(true);
			downloadButton->setEnabled(false);
			cancelButton->setEnabled(false);

			progressBar->setValue(mFileSize / mDivisor);
			filename = "[" + tr("LOCAL") + "] " + filename + " (" + misc::friendlyUnit(mFileSize) + ")";

			break;

		case SFI_STATE_UPLOAD:
			playButton->setEnabled(true);
			downloadButton->setEnabled(false);
			cancelButton->setEnabled(false);
			filename = "[" + tr("UPLOAD") + "] " + filename;

			break;
	}

	saveButton->hide();

	switch(mType)
	{
		case SFI_TYPE_CHANNEL:
		{
			saveButton->show();
			if (mMode == SFI_STATE_LOCAL)
			{
				saveButton->setEnabled(true);
			}
			else
			{
				saveButton->setEnabled(false);
			}
			if (mFlag & SFI_FLAG_CREATE) {
				cancelButton->setEnabled(true); // channel files which are extra files are removed
				cancelButton->setToolTip(tr("Remove Attachment"));
			}
		}
			break;
		case SFI_TYPE_ATTACH:
		{
			playButton->hide();
			downloadButton->hide();
			cancelButton->setEnabled(true);
			cancelButton->setToolTip(tr("Remove Attachment"));
		}
			break;
		default:
			break;
	}

	fileLabel->setText(filename);
	fileLabel->setToolTip(filename);
}

void SubFileItem::updateItem()
{
	/* fill in */
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::updateItem():" << mFileName;
	std::cerr << std::endl;
#endif

	/* Extract File Details */
	/* Update State if necessary */

	FileInfo fi;
	bool stateChanged = false;
	int msec_rate = 1000;

	if ((mMode == SFI_STATE_ERROR) || (mMode == SFI_STATE_LOCAL))
	{
#ifdef DEBUG_ITEM
		std::cerr << "SubFileItem::updateItem() STATE=Local/Error ignore";
		std::cerr << std::endl;
#endif
		/* ignore - dead file, or done */

		if (mMode == SFI_STATE_ERROR) {
			/* updateStatic once */
			stateChanged = true;
		}
	}
	else if (mMode == SFI_STATE_EXTRA)
	{
#ifdef DEBUG_ITEM
		std::cerr << "SubFileItem::updateItem() STATE=Extra File";
		std::cerr << std::endl;
#endif
		/* check for file status */
		if (rsFiles->ExtraFileStatus(mPath, fi))
		{
#ifdef DEBUG_ITEM
			std::cerr << "SubFileItem::updateItem() STATE=>Local";
			std::cerr << std::endl;
#endif
			mMode = SFI_STATE_LOCAL;

			/* fill in file details */
			mFileName = fi.fname;
			mFileSize = fi.size;
			mFileHash = fi.hash;

			/* have path already! */

			stateChanged = true;
		}
	}
	else
	{
		FileSearchFlags hintflags(0u) ;
		switch(mMode)
		{
			case SFI_STATE_REMOTE:
				hintflags = RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY;
				break;
			case SFI_STATE_DOWNLOAD:
				hintflags = RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY;
				break;
			case SFI_STATE_UPLOAD:
				hintflags = RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_SPEC_ONLY;
				break;
		}

		bool detailsOk = rsFiles->FileDetails(mFileHash, hintflags, fi);

		/* have details - see if state has changed */
		switch(mMode)
		{
			case SFI_STATE_REMOTE:
#ifdef DEBUG_ITEM
				std::cerr << "SubFileItem::updateItem() STATE=Remote";
				std::cerr << std::endl;
#endif
				/* is it downloading? */
				if (detailsOk)
				{
#ifdef DEBUG_ITEM
					std::cerr << "SubFileItem::updateItem() STATE=>Download";
					std::cerr << std::endl;
#endif
					/* downloading */
					mMode = SFI_STATE_DOWNLOAD;
					stateChanged = true;
				}
				break;
			case SFI_STATE_DOWNLOAD:
#ifdef DEBUG_ITEM
					std::cerr << "SubFileItem::updateItem() STATE=Download";
					std::cerr << std::endl;
#endif

				if (!detailsOk)
				{
#ifdef DEBUG_ITEM
					std::cerr << "SubFileItem::updateItem() STATE=>Remote";
					std::cerr << std::endl;
#endif
					mMode = SFI_STATE_REMOTE;
					stateChanged = true;
				}
				else
				{
					/* has it completed? */
					if (fi.avail == mFileSize)
					{
#ifdef DEBUG_ITEM
						std::cerr << "SubFileItem::updateItem() STATE=>Local";
						std::cerr << std::endl;
#endif
						/* save path */
						/* update progress */
						mMode = SFI_STATE_LOCAL;
						mPath = fi.path;
						stateChanged = true;
					}
					progressBar->setValue(fi.avail / mDivisor);
				}
				break;
			case SFI_STATE_UPLOAD:
#ifdef DEBUG_ITEM
				std::cerr << "SubFileItem::updateItem() STATE=Upload";
				std::cerr << std::endl;
#endif

				if (detailsOk)
				{
					progressBar->setValue(fi.avail / mDivisor);
				}

				/* update progress */
				break;
		}

	}

	/****** update based on new state ******/
	if (stateChanged)
	{
		updateItemStatic();
	}

	uint32_t repeat = 0;

	switch (mMode)
	{
		case SFI_STATE_ERROR:
			repeat = 0;
			break;

		case SFI_STATE_EXTRA:
			repeat = 1;
			msec_rate = 5000; /* slow */
			break;

		case SFI_STATE_REMOTE:
			repeat = 1;
			msec_rate = 30000; /* very slow */
			break;

		case SFI_STATE_DOWNLOAD:
			repeat = 1;
			msec_rate = 2000; /* should be download rate dependent */
			break;

		case SFI_STATE_LOCAL:
			repeat = 0;
			emit fileFinished(this);
			break;

		case SFI_STATE_UPLOAD:
			repeat = 1;
			msec_rate = 2000; /* should be download rate dependent */
			break;
	}

	
	if (repeat)
	{
#ifdef DEBUG_ITEM
		std::cerr << "SubFileItem::updateItem() callback for update!";
		std::cerr << std::endl;
#endif
	  	QTimer::singleShot( msec_rate, this, SLOT(updateItem( void ) ));
	}

}




void SubFileItem::smaller()
{
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::cancel()";
	std::cerr << std::endl;
#endif

#if 0
	expandFrame->hide();
#endif
}

void SubFileItem::toggle()
{
#if 0
	if (expandFrame->isHidden())
	{
		expandFrame->show();
	}
	else
	{
		expandFrame->hide();
	}
#endif
}

void SubFileItem::cancel()
{
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::cancel()";
	std::cerr << std::endl;
#endif
	//set the state to error mode
	mMode = SFI_STATE_ERROR;

	/* Only occurs - if it is downloading */
	if (((mType == SFI_TYPE_ATTACH) || (mType == SFI_TYPE_CHANNEL)) && (mFlag & SFI_FLAG_CREATE))
	{
		hide();
		rsFiles->ExtraFileRemove(FileHash(), RS_FILE_REQ_ANONYMOUS_ROUTING | RS_FILE_REQ_EXTRA);
		mPath = "";
	}
	else
	{
		rsFiles->FileCancel(mFileHash);
	}

	updateItem();
}


void SubFileItem::play()
{
	FileInfo info;
	FileSearchFlags flags = RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_NETWORK_WIDE;


	if (!rsFiles->FileDetails( mFileHash, flags, info))
		return;

	if (done()) {

		/* open file with a suitable application */
		QFileInfo qinfo;
		qinfo.setFile(info.path.c_str());
		if (qinfo.exists()) {
			if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
				std::cerr << "openTransfer(): can't open file " << info.path << std::endl;
			}
		}else{
			QMessageBox::information(this, tr("Play File"),
					tr("File %1 does not exist at location.").arg(info.path.c_str()));
			return;
		}
	} else {
		/* rise a message box for incompleted download file */
		QMessageBox::information(this, tr("Play File"),
				tr("File %1 is not completed.").arg(info.fname.c_str()));
		return;
	}

}

void SubFileItem::download()
{
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::download()";
	std::cerr << std::endl;
#endif

	std::list<std::string> sources ;

	std::string destination;
	if (!mChannelId.empty() && mType == SFI_TYPE_CHANNEL) {
		ChannelInfo ci;
		if (rsChannels->getChannelInfo(mChannelId, ci)) {
			destination = ci.destination_directory;
		}
	}

	// Add possible direct sources.
	//
	FileInfo finfo ;
	rsFiles->FileDetails(mFileHash,RS_FILE_HINTS_REMOTE,finfo) ;

	for(std::list<TransferInfo>::const_iterator it(finfo.peers.begin());it!=finfo.peers.end();++it)
		sources.push_back((*it).peerId) ;

	// TEMP
	std::cerr << "SubFileItem::download() Calling File Request";
	std::cerr << std::endl;

	if (mSrcId != "")
		sources.push_back(mSrcId);
	
	rsFiles->FileRequest(mFileName, mFileHash, mFileSize, destination, RS_FILE_REQ_ANONYMOUS_ROUTING, sources);

	downloadButton->setEnabled(false);

	updateItem();
}


void SubFileItem::save()
{
#ifdef DEBUG_ITEM
	std::cerr << "SubFileItem::save()";
	std::cerr << std::endl;
#endif

	FileInfo fInfo;

	if (mType == SFI_TYPE_CHANNEL)
	{
		/* only enable these function for Channels. */

		/* find out where they want to save it */
		QString startpath = "";
 		QString dir = QFileDialog::getExistingDirectory(this, tr("Save Channel File"),
						 startpath,
                                                 QFileDialog::ShowDirsOnly
                                                 | QFileDialog::DontResolveSymlinks);

		std::string destpath = dir.toStdString();

		if (destpath != "")
		{
			bool copied = rsFiles->ExtraFileMove(mFileName, mFileHash, mFileSize, destpath);

			// may be manually downloaded channel file
			if(!copied){
				rsFiles->FileDetails(mFileHash, RS_FILE_HINTS_NETWORK_WIDE | RS_FILE_HINTS_EXTRA, fInfo);

				if(fInfo.path != "")
				{

					destpath += "/" + fInfo.fname;
					rsFiles->copyFile(fInfo.path, destpath);
				}
			}
		}
	}
	else
	{
	}
}

uint32_t SubFileItem::getState() {
    return mMode;
}

bool SubFileItem::isDownloadable(bool &startable)
{
	/* Check buttons. Not good, but it works. */
	bool visible = downloadButton->isVisibleTo(this);
	startable = visible && downloadButton->isEnabled();

	return visible;
}

bool SubFileItem::isPlayable(bool &startable)
{
	/* Check buttons. Not good, but it works. */
	bool visible = playButton->isVisibleTo(this);
	startable = visible && playButton->isEnabled();

	return visible;
}

void SubFileItem::mediatype()
{
	/* check if the file is not a media file and change text */
	playButton->setText(tr("Open"));
	playButton->setToolTip(tr("Open File"));
}

void SubFileItem::copyLink()
{
	if (mFileName.empty() || mFileHash.empty()) {
		return;
	}

	RetroShareLink link;
	if (link.createFile(QString::fromUtf8(mFileName.c_str()), mFileSize, QString::fromStdString(mFileHash))) {
		QList<RetroShareLink> urls;
		urls.push_back(link);
		RSLinkClipboard::copyLinks(urls);
	}
}
