/*
 * libretroshare/src/ft: ftserver.cc
 *
 * File Transfer for RetroShare.
 *
 * Copyright 2008 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */

#include <sstream>
#include "util/rsdebug.h"
#include "util/rsdir.h"
#include "retroshare/rstypes.h"
const int ftserverzone = 29539;

#include "ft/ftserver.h"
#include "ft/ftextralist.h"
#include "ft/ftfilesearch.h"
#include "ft/ftcontroller.h"
#include "ft/ftfileprovider.h"
#include "ft/ftdatamultiplex.h"
//#include "ft/ftdwlqueue.h"
#include "turtle/p3turtle.h"


// Includes CacheStrapper / FiMonitor / FiStore for us.

#include "ft/ftdbase.h"

#include "pqi/pqi.h"
#include "pqi/p3connmgr.h"

#include "serialiser/rsserviceids.h"

#include <iostream>
#include <sstream>

/***
 * #define SERVER_DEBUG 1
 * #define DEBUG_TICK   1
 ***/

	/* Setup */
ftServer::ftServer(p3ConnectMgr *connMgr)
        :       mP3iface(NULL),
                mConnMgr(connMgr),
		mCacheStrapper(NULL),
		mFiStore(NULL), mFiMon(NULL),
		mFtController(NULL), mFtExtra(NULL),
		mFtDataplex(NULL), mFtSearch(NULL)
{
        mCacheStrapper = new ftCacheStrapper(connMgr);
}

void	ftServer::setConfigDirectory(std::string path)
{
	mConfigPath = path;

	/* Must update the sub classes ... if they exist
	 * TODO.
	 */

	std::string basecachedir = mConfigPath + "/cache";
	std::string localcachedir = mConfigPath + "/cache/local";
	std::string remotecachedir = mConfigPath + "/cache/remote";

	RsDirUtil::checkCreateDirectory(basecachedir) ;
	RsDirUtil::checkCreateDirectory(localcachedir) ;
	RsDirUtil::checkCreateDirectory(remotecachedir) ;
}

void	ftServer::setP3Interface(P3Interface *pqi)
{
	mP3iface = pqi;
}

	/* Control Interface */

	/* add Config Items (Extra, Controller) */
void	ftServer::addConfigComponents(p3ConfigMgr *mgr)
{
	/* NOT SURE ABOUT THIS ONE */
}

std::string ftServer::OwnId()
{
	std::string ownId;
	if (mConnMgr)
		ownId = mConnMgr->getOwnId();
	return ownId;
}

	/* Final Setup (once everything is assigned) */
void ftServer::SetupFtServer(NotifyBase *cb)
{

	/* setup FiStore/Monitor */
	std::string localcachedir = mConfigPath + "/cache/local";
	std::string remotecachedir = mConfigPath + "/cache/remote";
	std::string ownId = mConnMgr->getOwnId();

	/* search/extras List */
	mFtExtra = new ftExtraList();
	mFtSearch = new ftFileSearch();

	/* Transport */
	mFtDataplex = new ftDataMultiplex(ownId, this, mFtSearch);

	/* make Controller */
	mFtController = new ftController(mCacheStrapper, mFtDataplex, mConfigPath);
	mFtController -> setFtSearchNExtra(mFtSearch, mFtExtra);
	std::string tmppath = ".";
	mFtController->setPartialsDirectory(tmppath);
	mFtController->setDownloadDirectory(tmppath);


	/* Make Cache Source/Store */
	mFiStore = new ftFiStore(mCacheStrapper, mFtController, cb,mConnMgr, ownId, remotecachedir);
	mFiMon = new ftFiMonitor(mCacheStrapper,cb, localcachedir, ownId,mConfigPath);

	/* now add the set to the cachestrapper */
	CachePair cp(mFiMon, mFiStore, CacheId(RS_SERVICE_TYPE_FILE_INDEX, 0));
	mCacheStrapper -> addCachePair(cp);

	/* complete search setup */
	mFtSearch->addSearchMode(mCacheStrapper, RS_FILE_HINTS_CACHE);
	mFtSearch->addSearchMode(mFtExtra, RS_FILE_HINTS_EXTRA);
	mFtSearch->addSearchMode(mFiMon, RS_FILE_HINTS_LOCAL);
	mFtSearch->addSearchMode(mFiStore, RS_FILE_HINTS_REMOTE);

	mConnMgr->addMonitor(mFtController);
	mConnMgr->addMonitor(mCacheStrapper);

	return;
}

void ftServer::connectToTurtleRouter(p3turtle *fts)
{
	mTurtleRouter = fts ;

	mFtController->setTurtleRouter(fts) ;
}

void    ftServer::StartupThreads()
{
	/* start up order - important for dependencies */

	/* self contained threads */
	/* startup ExtraList Thread */
	mFtExtra->start();

	/* startup Monitor Thread */
	/* startup the FileMonitor (after cache load) */
	/* start it up */
	
	mFiMon->start();

	/* Controller thread */
	mFtController->start();

	/* Dataplex */
	mFtDataplex->start();

	/* start own thread */
	start();
}

void ftServer::StopThreads()
{
	/* stop own thread */
	join();

	/* stop Dataplex */
	mFtDataplex->join();

	/* stop Controller thread */
	mFtController->join();

	/* stop Monitor Thread */
	mFiMon->join();

	/* self contained threads */
	/* stop ExtraList Thread */
	mFtExtra->join();

	delete (mFtDataplex);
	mFtDataplex = NULL;

	delete (mFtController);
	mFtController = NULL;

	delete (mFiMon);
	mFiMon = NULL;

	delete (mFtExtra);
	mFtExtra = NULL;
}

CacheStrapper *ftServer::getCacheStrapper()
{
	return mCacheStrapper;
}

CacheTransfer *ftServer::getCacheTransfer()
{
	return mFtController;
}

void	ftServer::run()
{
	while(isRunning())
	{
		mFtDataplex->deleteUnusedServers() ;
#ifdef WIN32
		Sleep(5000);
#else
		sleep(5);
#endif
	}
}


	/***************************************************************/
	/********************** RsFiles Interface **********************/
	/***************************************************************/


	/***************************************************************/
	/********************** Controller Access **********************/
	/***************************************************************/

bool ftServer::checkHash(const std::string& hash,std::string& error_string)
{
	static const uint32_t HASH_LENGTH = 40 ;

	if(hash.length() != HASH_LENGTH)
	{
		std::ostringstream is ;
		is << "Line too long : " << hash.length() << " chars, " << HASH_LENGTH << " expected." ;
		is.flush() ;
		error_string = is.str() ;
		return false ;
	}

	for(uint32_t i=0;i<hash.length();++i)
		if(!((hash[i] > 47 && hash[i] < 58) || (hash[i] > 96 && hash[i] < 103)))
		{
			std::ostringstream is;
			is << "unexpected char code=" << (int)hash[i] << " '" << hash[i] << "'" ;
			is.flush() ;
			error_string = is.str() ;
			return false ;
		}

	return true ;
}

bool ftServer::alreadyHaveFile(const std::string& hash, FileInfo &info)
{
	return mFtController->alreadyHaveFile(hash, info);
}

bool ftServer::FileRequest(const std::string& fname, const std::string& hash, uint64_t size, const std::string& dest, uint32_t flags, const std::list<std::string>& srcIds)
{
	std::string error_string ;

	if(!checkHash(hash,error_string))
	{
		rsicontrol->getNotify().notifyErrorMsg(0,0,"Error handling hash \""+hash+"\". This hash appears to be invalid(Error string=\""+error_string+"\"). This is probably due an bad handling of strings.") ;
		return false ;
	}

   std::cerr << "Requesting " << fname << std::endl ;

	if(!mFtController->FileRequest(fname, hash, size, dest, flags, srcIds))
		return false ;

	return true ;
}

bool ftServer::setChunkStrategy(const std::string& hash,FileChunksInfo::ChunkStrategy s)
{
	return mFtController->setChunkStrategy(hash,s);
}
uint32_t ftServer::freeDiskSpaceLimit()const
{
	return mFtController->freeDiskSpaceLimit() ;
}
void ftServer::setFreeDiskSpaceLimit(uint32_t s)
{
	mFtController->setFreeDiskSpaceLimit(s) ;
}
void ftServer::setDefaultChunkStrategy(FileChunksInfo::ChunkStrategy s) 
{
	mFtController->setDefaultChunkStrategy(s) ;
}
FileChunksInfo::ChunkStrategy ftServer::defaultChunkStrategy() 
{
	return mFtController->defaultChunkStrategy() ;
}
bool ftServer::FileCancel(const std::string& hash)
{
	// Remove from both queue and ftController, by default.
	//
	mFtController->FileCancel(hash);

	return true ;
}

bool ftServer::FileControl(const std::string& hash, uint32_t flags)
{
	return mFtController->FileControl(hash, flags);
}

bool ftServer::FileClearCompleted()
{
	return mFtController->FileClearCompleted();
}

void ftServer::setQueueSize(uint32_t s)
{
	mFtController->setQueueSize(s) ;
}
uint32_t ftServer::getQueueSize()
{
	return mFtController->getQueueSize() ;
}
	/* Control of Downloads Priority. */
bool ftServer::changeQueuePosition(const std::string hash, QueueMove mv)
{
	mFtController->moveInQueue(hash,mv) ;
	return true ;
}
bool ftServer::changeDownloadSpeed(const std::string hash, int speed)
{
	mFtController->setPriority(hash, (DwlSpeed)speed);
	return true ;
}
bool ftServer::getDownloadSpeed(const std::string hash, int & speed)
{
	DwlSpeed _speed;
	int ret = mFtController->getPriority(hash, _speed);
	if (ret) 
		speed = _speed;

	return ret;
}
bool ftServer::clearDownload(const std::string hash)
{
   return true ;
}

bool ftServer::FileDownloadChunksDetails(const std::string& hash,FileChunksInfo& info)
{
	return mFtController->getFileDownloadChunksDetails(hash,info);
}

	/* Directory Handling */
void ftServer::setDownloadDirectory(std::string path)
{
	mFtController->setDownloadDirectory(path);
}

std::string ftServer::getDownloadDirectory()
{
	return mFtController->getDownloadDirectory();
}

void ftServer::setPartialsDirectory(std::string path)
{
	mFtController->setPartialsDirectory(path);
}

std::string ftServer::getPartialsDirectory()
{
	return mFtController->getPartialsDirectory();
}


	/***************************************************************/
	/************************* Other Access ************************/
	/***************************************************************/

bool ftServer::FileDownloads(std::list<std::string> &hashs)
{
	return mFtController->FileDownloads(hashs);
	/* this only contains downloads.... not completed */
	//return mFtDataplex->FileDownloads(hashs);
}

bool ftServer::FileUploadChunksDetails(const std::string& hash,const std::string& peer_id,CompressedChunkMap& cmap)
{
	return mFtDataplex->getClientChunkMap(hash,peer_id,cmap);
}

bool ftServer::FileUploads(std::list<std::string> &hashs)
{
	return mFtDataplex->FileUploads(hashs);
}

bool ftServer::FileDetails(const std::string &hash, uint32_t hintflags, FileInfo &info)
{
	if (hintflags & RS_FILE_HINTS_DOWNLOAD)
		if(mFtController->FileDetails(hash, info))
			return true ;

	if(hintflags & RS_FILE_HINTS_UPLOAD)
		if(mFtDataplex->FileDetails(hash, hintflags, info))
			return true ;

	if(hintflags & ~(RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_DOWNLOAD)) 
		if(mFtSearch->search(hash, hintflags, info))
			return true ;

	return false;
}

	/***************************************************************/
	/******************* ExtraFileList Access **********************/
	/***************************************************************/

bool  ftServer::ExtraFileAdd(std::string fname, std::string hash, uint64_t size,
						uint32_t period, uint32_t flags)
{
	return mFtExtra->addExtraFile(fname, hash, size, period, flags);
}

bool ftServer::ExtraFileRemove(std::string hash, uint32_t flags)
{
	return mFtExtra->removeExtraFile(hash, flags);
}

bool ftServer::ExtraFileHash(std::string localpath, uint32_t period, uint32_t flags)
{
	return mFtExtra->hashExtraFile(localpath, period, flags);
}

bool ftServer::ExtraFileStatus(std::string localpath, FileInfo &info)
{
	return mFtExtra->hashExtraFileDone(localpath, info);
}

bool ftServer::ExtraFileMove(std::string fname, std::string hash, uint64_t size,
                                std::string destpath)
{
	return mFtExtra->moveExtraFile(fname, hash, size, destpath);
}


	/***************************************************************/
	/******************** Directory Listing ************************/
	/***************************************************************/

int ftServer::RequestDirDetails(std::string uid, std::string path, DirDetails &details)
{
#ifdef SERVER_DEBUG
	std::cerr << "ftServer::RequestDirDetails(uid:" << uid;
	std::cerr << ", path:" << path << ", ...) -> mFiStore";
	std::cerr << std::endl;

	if (!mFiStore)
	{
		std::cerr << "mFiStore not SET yet = FAIL";
		std::cerr << std::endl;
	}
#endif
	return mFiStore->RequestDirDetails(uid, path, details);
}

int ftServer::RequestDirDetails(void *ref, DirDetails &details, uint32_t flags)
{
#ifdef SERVER_DEBUG
	std::cerr << "ftServer::RequestDirDetails(ref:" << ref;
	std::cerr << ", flags:" << flags << ", ...) -> mFiStore";
	std::cerr << std::endl;

	if (!mFiStore)
	{
		std::cerr << "mFiStore not SET yet = FAIL";
		std::cerr << std::endl;
	}

#endif
	if(flags & DIR_FLAGS_LOCAL)
		return mFiMon->RequestDirDetails(ref, details, flags);
	else
		return mFiStore->RequestDirDetails(ref, details, flags);
}
uint32_t ftServer::getType(void *ref, uint32_t flags)
{
#ifdef SERVER_DEBUG
	std::cerr << "ftServer::RequestDirDetails(ref:" << ref;
	std::cerr << ", flags:" << flags << ", ...) -> mFiStore";
	std::cerr << std::endl;

	if (!mFiStore)
	{
		std::cerr << "mFiStore not SET yet = FAIL";
		std::cerr << std::endl;
	}

#endif
	if(flags & DIR_FLAGS_LOCAL)
		return mFiMon->getType(ref);
	else
		return mFiStore->getType(ref);
}
	/***************************************************************/
	/******************** Search Interface *************************/
	/***************************************************************/


int ftServer::SearchKeywords(std::list<std::string> keywords, std::list<DirDetails> &results,uint32_t flags)
{
#ifdef SERVER_DEBUG
	std::cerr << "ftServer::SearchKeywords()";
	std::cerr << std::endl;

	if (!mFiStore)
	{
		std::cerr << "mFiStore not SET yet = FAIL";
		std::cerr << std::endl;
	}

#endif
	if(flags & DIR_FLAGS_LOCAL)
		return mFiMon->SearchKeywords(keywords, results,flags);
	else
		return mFiStore->SearchKeywords(keywords, results,flags);
}

int ftServer::SearchBoolExp(Expression * exp, std::list<DirDetails> &results,uint32_t flags)
{
	if(flags & DIR_FLAGS_LOCAL)
		return mFiMon->SearchBoolExp(exp,results,flags) ;
	else
		return mFiStore->searchBoolExp(exp, results);
}


	/***************************************************************/
	/*************** Local Shared Dir Interface ********************/
	/***************************************************************/

bool    ftServer::ConvertSharedFilePath(std::string path, std::string &fullpath)
{
	return mFiMon->convertSharedFilePath(path, fullpath);
}

void    ftServer::ForceDirectoryCheck()
{
	mFiMon->forceDirectoryCheck();
	return;
}

bool    ftServer::InDirectoryCheck()
{
	return mFiMon->inDirectoryCheck();
}

bool ftServer::CopyFile(const std::string& source, const std::string& dest)
{
	return mFtController->copyFile(source, dest);
}

bool	ftServer::getSharedDirectories(std::list<SharedDirInfo> &dirs)
{
	mFiMon->getSharedDirectories(dirs);
	return true;
}

bool	ftServer::setSharedDirectories(std::list<SharedDirInfo> &dirs)
{
	mFiMon->setSharedDirectories(dirs);
	return true;
}

bool 	ftServer::addSharedDirectory(const SharedDirInfo& dir)
{
	SharedDirInfo _dir = dir;
	_dir.filename = RsDirUtil::convertPathToUnix(_dir.filename);

	std::list<SharedDirInfo> dirList;
	mFiMon->getSharedDirectories(dirList);

	// check that the directory is not already in the list.
	for(std::list<SharedDirInfo>::const_iterator it(dirList.begin());it!=dirList.end();++it)
		if((*it).filename == _dir.filename)
			return false ;

	// ok then, add the shared directory.
	dirList.push_back(_dir);

	mFiMon->setSharedDirectories(dirList);
	return true;
}

bool ftServer::updateShareFlags(const SharedDirInfo& info)
{
	mFiMon->updateShareFlags(info);

	return true ;
}

bool 	ftServer::removeSharedDirectory(std::string dir)
{
	dir = RsDirUtil::convertPathToUnix(dir);

	std::list<SharedDirInfo> dirList;
	std::list<SharedDirInfo>::iterator it;

#ifdef SERVER_DEBUG
	std::cerr << "ftServer::removeSharedDirectory(" << dir << ")";
	std::cerr << std::endl;
#endif

	mFiMon->getSharedDirectories(dirList);

#ifdef SERVER_DEBUG
	for(it = dirList.begin(); it != dirList.end(); it++)
	{
		std::cerr << "ftServer::removeSharedDirectory()";
		std::cerr << " existing: " << (*it).filename;
		std::cerr << std::endl;
	}
#endif

	for(it = dirList.begin();it!=dirList.end() && (*it).filename != dir;++it) ;

	if(it == dirList.end())
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::removeSharedDirectory()";
		std::cerr << " Cannot Find Directory... Fail";
		std::cerr << std::endl;
#endif

		return false;
	}


#ifdef SERVER_DEBUG
	std::cerr << "ftServer::removeSharedDirectory()";
	std::cerr << " Updating Directories";
	std::cerr << std::endl;
#endif

	dirList.erase(it);
	mFiMon->setSharedDirectories(dirList);

	return true;
}
void	ftServer::setWatchPeriod(int minutes) 
{
	mFiMon->setWatchPeriod(minutes*60) ;
}
int ftServer::watchPeriod() const
{
	return mFiMon->watchPeriod()/60 ;
}

void	ftServer::setRememberHashFiles(bool b) 
{
	mFiMon->setRememberHashCache(b) ;
}
bool ftServer::rememberHashFiles() const
{
	return mFiMon->rememberHashCache() ;
}
void	ftServer::setRememberHashFilesDuration(uint32_t days) 
{
	mFiMon->setRememberHashCacheDuration(days) ;
}
uint32_t ftServer::rememberHashFilesDuration() const 
{
	return mFiMon->rememberHashCacheDuration() ;
}
void   ftServer::clearHashCache() 
{
	mFiMon->clearHashCache() ;
}

bool ftServer::getShareDownloadDirectory()
{
	std::list<SharedDirInfo> dirList;
	mFiMon->getSharedDirectories(dirList);

	std::string dir = mFtController->getDownloadDirectory();

	// check if the download directory is in the list.
	for (std::list<SharedDirInfo>::const_iterator it(dirList.begin()); it != dirList.end(); ++it)
		if ((*it).filename == dir)
			return true;

	return false;
}

bool ftServer::shareDownloadDirectory(bool share)
{
	if (share) {
		/* Share */
		SharedDirInfo inf ;
		inf.filename = mFtController->getDownloadDirectory();
		inf.shareflags = RS_FILE_HINTS_NETWORK_WIDE | RS_FILE_HINTS_BROWSABLE ;

		return addSharedDirectory(inf);
	}

	/* Unshare */
	std::string dir = mFtController->getDownloadDirectory();
	return removeSharedDirectory(dir);
}

	/***************************************************************/
	/****************** End of RsFiles Interface *******************/
	/***************************************************************/


	/***************************************************************/
	/**************** Config Interface *****************************/
	/***************************************************************/

        /* Key Functions to be overloaded for Full Configuration */
RsSerialiser *ftServer::setupSerialiser()
{
	return NULL;
}

bool ftServer::saveList(bool &cleanup, std::list<RsItem *>& list)
{

	return true;
}

bool    ftServer::loadList(std::list<RsItem *>& load)
{
	return true;
}

bool  ftServer::loadConfigMap(std::map<std::string, std::string> &configMap)
{
	return true;
}


	/***************************************************************/
	/**********************     Data Flow     **********************/
	/***************************************************************/

	/* Client Send */
bool	ftServer::sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize)
{
	if(mTurtleRouter->isTurtlePeer(peerId))
		mTurtleRouter->sendDataRequest(peerId,hash,size,offset,chunksize) ;
	else
	{
		/* create a packet */
		/* push to networking part */
		RsFileRequest *rfi = new RsFileRequest();

		/* id */
		rfi->PeerId(peerId);

		/* file info */
		rfi->file.filesize   = size;
		rfi->file.hash       = hash; /* ftr->hash; */

		/* offsets */
		rfi->fileoffset = offset; /* ftr->offset; */
		rfi->chunksize  = chunksize; /* ftr->chunk; */

		mP3iface->SendFileRequest(rfi);
	}

	return true;
}

bool ftServer::sendChunkMapRequest(const std::string& peerId,const std::string& hash,bool is_client)
{
	if(mTurtleRouter->isTurtlePeer(peerId))
		mTurtleRouter->sendChunkMapRequest(peerId,hash,is_client) ;
	else
	{
		/* create a packet */
		/* push to networking part */
		RsFileChunkMapRequest *rfi = new RsFileChunkMapRequest();

		/* id */
		rfi->PeerId(peerId);

		/* file info */
		rfi->hash = hash; /* ftr->hash; */
		rfi->is_client = is_client ;

		mP3iface->SendFileChunkMapRequest(rfi);
	}

	return true ;
}

bool ftServer::sendChunkMap(const std::string& peerId,const std::string& hash,const CompressedChunkMap& map,bool is_client)
{
	if(mTurtleRouter->isTurtlePeer(peerId))
		mTurtleRouter->sendChunkMap(peerId,hash,map,is_client) ;
	else
	{
		/* create a packet */
		/* push to networking part */
		RsFileChunkMap *rfi = new RsFileChunkMap();

		/* id */
		rfi->PeerId(peerId);

		/* file info */
		rfi->hash = hash; /* ftr->hash; */
		rfi->is_client = is_client; /* ftr->hash; */
		rfi->compressed_map = map; /* ftr->hash; */

		mP3iface->SendFileChunkMap(rfi);
	}

	return true ;
}
bool ftServer::sendCRC32MapRequest(const std::string& peerId,const std::string& hash)
{
	if(mTurtleRouter->isTurtlePeer(peerId))
		mTurtleRouter->sendCRC32MapRequest(peerId,hash) ;
	else
	{
		/* create a packet */
		/* push to networking part */
		RsFileCRC32MapRequest *rfi = new RsFileCRC32MapRequest();

		/* id */
		rfi->PeerId(peerId);

		/* file info */
		rfi->hash = hash; /* ftr->hash; */

		mP3iface->SendFileCRC32MapRequest(rfi);
	}

	// We only send chunkmap requests to turtle peers. This will be a problem at display time for
	// direct friends, so I'll see later whether I code it or not.
	return true ;
}

bool ftServer::sendCRC32Map(const std::string& peerId,const std::string& hash,const CRC32Map& crcmap)
{
	if(mTurtleRouter->isTurtlePeer(peerId))
		mTurtleRouter->sendCRC32Map(peerId,hash,crcmap) ;
	else
	{
		/* create a packet */
		/* push to networking part */
		RsFileCRC32Map *rfi = new RsFileCRC32Map();

		/* id */
		rfi->PeerId(peerId);

		/* file info */
		rfi->hash = hash; /* ftr->hash; */
		rfi->crc_map = crcmap; /* ftr->hash; */

		mP3iface->SendFileCRC32Map(rfi);
	}

	// We only send chunkmap requests to turtle peers. This will be a problem at display time for
	// direct friends, so I'll see later whether I code it or not.
	return true ;
}

//const uint32_t	MAX_FT_CHUNK  = 32 * 1024; /* 32K */
//const uint32_t	MAX_FT_CHUNK  = 16 * 1024; /* 16K */
const uint32_t	MAX_FT_CHUNK  = 8 * 1024; /* 16K */

	/* Server Send */
bool	ftServer::sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t baseoffset, uint32_t chunksize, void *data)
{
	/* create a packet */
	/* push to networking part */
	uint32_t tosend = chunksize;
	uint64_t offset = 0;
	uint32_t chunk;

#ifdef SERVER_DEBUG
	std::cerr << "ftServer::sendData() to " << peerId << std::endl;
	std::cerr << "hash: " << hash;
	std::cerr << " offset: " << baseoffset;
	std::cerr << " chunk: " << chunksize;
	std::cerr << " data: " << data;
	std::cerr << std::endl;
#endif

	while(tosend > 0)
	{
		/* workout size */
		chunk = MAX_FT_CHUNK;
		if (chunk > tosend)
		{
			chunk = tosend;
		}

		/******** New Serialiser Type *******/

		if(mTurtleRouter->isTurtlePeer(peerId))
			mTurtleRouter->sendFileData(peerId,hash,size,baseoffset+offset,chunk,&(((uint8_t *) data)[offset])) ;
		else
		{
			RsFileData *rfd = new RsFileData();

			/* set id */
			rfd->PeerId(peerId);

			/* file info */
			rfd->fd.file.filesize = size;
			rfd->fd.file.hash     = hash;
			rfd->fd.file.name     = ""; /* blank other data */
			rfd->fd.file.path     = "";
			rfd->fd.file.pop      = 0;
			rfd->fd.file.age      = 0;

			rfd->fd.file_offset = baseoffset + offset;

			/* file data */
			rfd->fd.binData.setBinData( &(((uint8_t *) data)[offset]), chunk);

			mP3iface->SendFileData(rfd);

			/* print the data pointer */
#ifdef SERVER_DEBUG
			std::cerr << "ftServer::sendData() Packet: " << std::endl;
			std::cerr << " offset: " << rfd->fd.file_offset;
			std::cerr << " chunk: " << chunk;
			std::cerr << " len: " << rfd->fd.binData.bin_len;
			std::cerr << " data: " << rfd->fd.binData.bin_data;
			std::cerr << std::endl;
#endif
		}

		offset += chunk;
		tosend -= chunk;
	}

	/* clean up data */
	free(data);

	return true;
}


/* NB: The rsCore lock must be activated before calling this.
 * This Lock should be moved lower into the system...
 * most likely destination is in ftServer.
 */
int	ftServer::tick()
{
	rslog(RSL_DEBUG_BASIC, ftserverzone,
		"filedexserver::tick()");

	if (mP3iface == NULL)
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::tick() ERROR: mP3iface == NULL";
#endif

		std::ostringstream out;
		rslog(RSL_DEBUG_BASIC, ftserverzone,
			"filedexserver::tick() Invalid Interface()");

		return 1;
	}

	int moreToTick = 0;

	if (0 < mP3iface -> tick())
	{
		moreToTick = 1;
#ifdef DEBUG_TICK
		std::cerr << "filedexserver::tick() moreToTick from mP3iface" << std::endl;
#endif
	}

	if (0 < handleInputQueues())
	{
		moreToTick = 1;
#ifdef DEBUG_TICK
		std::cerr << "filedexserver::tick() moreToTick from InputQueues" << std::endl;
#endif
	}
	return moreToTick;
}


// This function needs to be divided up.
bool    ftServer::handleInputQueues()
{
	bool moreToTick = false;

	if (handleCacheData())
		moreToTick = true;

	if (handleFileData())
		moreToTick = true;

	return moreToTick;
}

bool     ftServer::handleCacheData()
{
	// get all the incoming results.. and print to the screen.
	RsCacheRequest *cr;
	RsCacheItem    *ci;

	// Loop through Search Results.
	int i = 0;
	int i_init = 0;

#ifdef SERVER_DEBUG
	//std::cerr << "ftServer::handleCacheData()" << std::endl;
#endif
	while((ci = mP3iface -> GetSearchResult()) != NULL)
	{

#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleCacheData() Recvd SearchResult (CacheResponse!)" << std::endl;
		std::ostringstream out;
		if (i++ == i_init)
		{
			out << "Recieved Search Results:" << std::endl;
		}
		ci -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif

		/* these go to the CacheStrapper! */
		CacheData data;
		data.pid = ci->PeerId();
		peerConnectState pca;
		mConnMgr->getFriendNetStatus(ci->PeerId(), pca);
		data.pname = pca.name;
		data.cid = CacheId(ci->cacheType, ci->cacheSubId);
		data.path = ci->file.path;
		data.name = ci->file.name;
		data.hash = ci->file.hash;
		data.size = ci->file.filesize;
		data.recvd = time(NULL) ;

		mCacheStrapper->recvCacheResponse(data, time(NULL));

		delete ci;
	}

	// now requested Searches.
	i_init = i;
	while((cr = mP3iface -> RequestedSearch()) != NULL)
	{
#ifdef SERVER_DEBUG
		/* just delete these */
		std::ostringstream out;
		out << "Requested Search:" << std::endl;
		cr -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		delete cr;
	}


	// Now handle it replacement (pushed cache results)
	{
		std::list<std::pair<RsPeerId, CacheData> > cacheUpdates;
		std::list<std::pair<RsPeerId, CacheData> >::iterator it;

		mCacheStrapper->getCacheUpdates(cacheUpdates);
		for(it = cacheUpdates.begin(); it != cacheUpdates.end(); it++)
		{
			/* construct reply */
			RsCacheItem *ci = new RsCacheItem();

			/* id from incoming */
			ci -> PeerId(it->first);

			ci -> file.hash = (it->second).hash;
			ci -> file.name = (it->second).name;
			ci -> file.path = ""; // (it->second).path;
			ci -> file.filesize = (it->second).size;
			ci -> cacheType  = (it->second).cid.type;
			ci -> cacheSubId =  (it->second).cid.subid;

#ifdef SERVER_DEBUG
			std::ostringstream out2;
			out2 << "Outgoing CacheStrapper Update -> RsCacheItem:" << std::endl;
			ci -> print(out2);
			std::cerr << out2.str() << std::endl;
#endif

			//rslog(RSL_DEBUG_BASIC, ftserverzone, out2.str());
			mP3iface -> SendSearchResult(ci);

			i++;
		}
	}
	return (i > 0);
}


bool    ftServer::handleFileData()
{
	// now File Input.
	RsFileRequest *fr;
	RsFileData *fd;
	RsFileChunkMapRequest *fcmr;
	RsFileChunkMap *fcm;
	RsFileCRC32MapRequest *fccrcmr;
	RsFileCRC32Map *fccrcm;

	int i_init = 0;
	int i = 0;

	i_init = i;
	while((fr = mP3iface -> GetFileRequest()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ftFiler Request" << std::endl;
		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Item:" << std::endl;
		}
		fr -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif

		i++; /* count */
		mFtDataplex->recvDataRequest(fr->PeerId(),
				fr->file.hash,  fr->file.filesize,
				fr->fileoffset, fr->chunksize);

FileInfo(ffr);
		delete fr;
	}

	// now File Data.
	i_init = i;
	while((fd = mP3iface -> GetFileData()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ftFiler Data" << std::endl;
		std::cerr << "hash: " << fd->fd.file.hash;
		std::cerr << " length: " << fd->fd.binData.bin_len;
		std::cerr << " data: " << fd->fd.binData.bin_data;
		std::cerr << std::endl;

		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Data:" << std::endl;
		}
		fd -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		i++; /* count */

		/* incoming data */
		mFtDataplex->recvData(fd->PeerId(),
				 fd->fd.file.hash,  fd->fd.file.filesize,
				fd->fd.file_offset,
				fd->fd.binData.bin_len,
				fd->fd.binData.bin_data);

		/* we've stolen the data part -> so blank before delete
		 */
		fd->fd.binData.TlvShallowClear();
		delete fd;
	}
	// now file chunkmap requests
	i_init = i;
	while((fcmr = mP3iface -> GetFileChunkMapRequest()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ChunkMap request" << std::endl;
		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Data:" << std::endl;
		}
		fcmr -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		i++; /* count */

		/* incoming data */
		mFtDataplex->recvChunkMapRequest(fcmr->PeerId(), fcmr->hash,fcmr->is_client) ;

		delete fcmr;
	}
	// now file chunkmaps 
	i_init = i;
	while((fcm = mP3iface -> GetFileChunkMap()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ChunkMap request" << std::endl;
		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Data:" << std::endl;
		}
		fcm -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		i++; /* count */

		/* incoming data */
		mFtDataplex->recvChunkMap(fcm->PeerId(), fcm->hash,fcm->compressed_map,fcm->is_client) ;

		delete fcm;
	}
	// now file chunkmap requests
	i_init = i;
	while((fccrcmr = mP3iface -> GetFileCRC32MapRequest()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ChunkMap request" << std::endl;
		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Data:" << std::endl;
		}
		fccrcmr -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		i++; /* count */

		/* incoming data */
		mFtDataplex->recvCRC32MapRequest(fccrcmr->PeerId(), fccrcmr->hash) ;

		delete fccrcmr;
	}
	// now file chunkmaps 
	i_init = i;
	while((fccrcm = mP3iface -> GetFileCRC32Map()) != NULL )
	{
#ifdef SERVER_DEBUG
		std::cerr << "ftServer::handleFileData() Recvd ChunkMap request" << std::endl;
		std::ostringstream out;
		if (i == i_init)
		{
			out << "Incoming(Net) File Data:" << std::endl;
		}
		fccrcm -> print(out);
		rslog(RSL_DEBUG_BASIC, ftserverzone, out.str());
#endif
		i++; /* count */

		/* incoming data */
		mFtDataplex->recvCRC32Map(fccrcm->PeerId(), fccrcm->hash,fccrcm->crc_map); 

		delete fccrcm;
	}
	if (i > 0)
	{
		return 1;
	}
	return 0;
}

/**********************************
 **********************************
 **********************************
 *********************************/

 /***************************** CONFIG ****************************/

bool    ftServer::addConfiguration(p3ConfigMgr *cfgmgr)
{
	/* add all the subbits to config mgr */
	cfgmgr->addConfiguration("ft_shared.cfg", mFiMon);
	cfgmgr->addConfiguration("ft_extra.cfg", mFtExtra);
	cfgmgr->addConfiguration("ft_transfers.cfg", mFtController);

	return true;
}

bool	ftServer::ResumeTransfers()
{
	mFtController->activate();

	return true;
}

