#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <kapp.h>
#include "archprot.h"

ArchiveProtocol::ArchiveProtocol(const char *localFile, const char *file)
	: Protocol(Protocol::Arch)
{
	LocalFile = localFile;
	File = file;
	CurrentPath = "";
	rawEntries = new FileInfoList;
	rawEntries->setAutoDelete(TRUE);
	entries = new FileInfoList;
	entries->setAutoDelete(TRUE);
	namedEntries = new QStrList(TRUE);
	namedEntries->setAutoDelete(TRUE);
	DirDict = new QDict<TreeItem>;
	DirDict->setAutoDelete(TRUE);
	updateFlag = filterFlag = TRUE;
	Prefix = file;
	ProcID = 0;
	fout = 0;
}

ArchiveProtocol::~ArchiveProtocol()
{
	delete rawEntries;
	delete entries;
	delete namedEntries;
	delete DirDict;
}

bool ArchiveProtocol::setupCommunication()
{
	if (pipe(out) < 0) return FALSE;
	if (pipe(err) < 0) return FALSE;
	return TRUE;
}

bool ArchiveProtocol::CommDoneChild()
{
	::close(out[0]);
	::close(err[0]);
	dup2(out[1],STDOUT_FILENO);
	dup2(err[1],STDERR_FILENO);
	return TRUE;
}

bool ArchiveProtocol::CommDoneParent()
{
	::close(out[1]);
	::close(err[1]);
	return TRUE;
}

bool ArchiveProtocol::CommDone()
{
	::close(out[0]);
	::close(err[0]);
	return TRUE;
}

bool ArchiveProtocol::executeCommand(const char *prog, QStrList& args)
{
cout << "executing : " << prog << endl;
	Buffer.truncate(0);
	char	**arglist = new char*[args.count()+1];
	QStrListIterator	it(args);
	for (int i=0;it.current();++it,i++) arglist[i] = it.current();
	arglist[args.count()] = 0;
	if (!setupCommunication()) return FALSE;
	ProcID = fork();
	if (ProcID == 0) {	// Child process
		if (!CommDoneChild()) exit(-1);
		execvp(prog,arglist);
		exit(-1);
	}
	else {			// Parent process
		delete [] arglist;
		if (!CommDoneParent()) return FALSE;
		fout = fdopen(out[0],"r");
		return TRUE;
	}
}

bool ArchiveProtocol::finishCommand()
{
	CommDone();
	fclose(fout);
	fout = 0;
	return TRUE;
}

void* ArchiveProtocol::thread_protocolCmd(ProtocolCmd *cmd)
{
	switch (cmd->code) {
	   case Prot_List:
		cmd->out = (void*)thread_updateEntries();
		break;
	   case Prot_CopyFrom:
		cmd->out = (void*)thread_copyFromProtocol((const char*)cmd->parg1,(const char *)cmd->parg2,(bool)cmd->parg3,(ProgressDlg*)cmd->parg4);
		break;
	}

	cmd->done = TRUE;
	return 0;
}

void ArchiveProtocol::thread_protocolCleanup(ProtocolCmd *cmd)
{
	cmd->prot->cleanup();
	cmd->out = (void*)0;
	cmd->done = TRUE;
}

bool ArchiveProtocol::updateEntries()
{
	ProtocolCmd	*cmd = new ProtocolCmd(Prot_List,this);
	launchThread(cmd);
	bool	retval = cmd->done && (bool)cmd->out;
	delete cmd;
	th_ID = 0;
	if (retval) updateFlag = FALSE;
	return retval;
}

bool ArchiveProtocol::updateFilteredEntries(bool hidden, bool dirsOnly, bool showArchive)
{
	entries->clear();
	TreeItem	*item;
cout << "Current path : " << CurrentPath << endl;
	if (!CurrentPath.isEmpty()) {
		item = DirDict->find(CurrentPath.data());
		if (!item) item = DirDict->find(CurrentPath.right(CurrentPath.length()-1).data());
	}
	else item = DirDict->find("/");
	if (item) {
		SubProtocol	*subProt;
		FileInfoListIterator	it(*(item->List));
		for (;it.current();++it) {
			if (!hidden && it.current()->fileName()[0] == '.') continue;
			if (!it.current()->isDir() && dirsOnly && (!showArchive ||
			    (subProt=matchFileName(it.current()->fileName(),ProtList)) == 0))
				continue;
			FileInfo	*fi = new FileInfo(*(it.current()));
			if (!fi->isDir() && dirsOnly && showArchive) {
				QString		complete(fi->absFilePath().data());
				complete += '#';
				complete += subProt->protocol;
				complete += ':';
				fi->setPath(complete.data());
			}
			entries->append(fi);
		}
		return TRUE;
	}
	return FALSE;
}

void ArchiveProtocol::processEntry(FileInfo *fi)
{
	QString		Name = fi->fileName().data(), newName;
	int	pos(0);
	if (fi->isDir()) {
		TreeItem	*item = new TreeItem(Name.data());
		DirDict->insert(Name.data(),item);
		pos = Name.findRev('/',Name.length()-2);
	}
	else pos = Name.findRev('/',Name.length()-1);
	if (pos != -1) {
		QString		parentName = Name.left(pos+1);
		TreeItem	*parent = DirDict->find(parentName.data());
		if (parent) parent->List->append(fi);
		else {
			QString		str;
			if (Code == 5) {		// Rpm case
				QString		tmp;
				createParentStr(tmp,fi);
				str.sprintf("%s %s",parentName.data(),tmp.data());
			}
			else {
				createParentStr(str,fi);
				str += parentName;
			}
cout << "creating dir : " << str << endl;
			FileInfo	*pfi = new FileInfo(str.data(),Code);
			str = Prefix.data();
			str += pfi->fileName();
			if (pfi->isDir() && str[str.length()-1] != '/') str += '/';
			pfi->setPath(str.data());
			rawEntries->append(pfi);
			processEntry(pfi);
			parent = DirDict->find(parentName.data());
			parent->List->append(fi);
		}
	}
	else {
		TreeItem	*parent = DirDict->find("/");
		parent->List->append(fi);
	}
	newName = Name.mid(pos+1,Name.length());
	if (fi->isDir() && newName[newName.length()-1] == '/') newName.truncate(newName.length()-1);
	fi->setFileName(newName.data());
	KMimeExtension	*ext;
	if (fi->isFile()) {
		ext = getMimeTypeFromExtension(fi);
		if (ext == 0) ext = getMimeTypeFromName(fi);
		if (ext != 0) fi->setDescription(ext->Description.data());
		else fi->setDescription(i18n("Unknown"));
	}
	else {
		ext = getMimeTypeFromNode(fi);
		if (ext != 0) fi->setDescription(ext->Description.data());
		else if (fi->isDir()) fi->setDescription(i18n("Directory"));
		else fi->setDescription(i18n("Unknown"));
	}
	fi->setMimeType(ext);
}

bool ArchiveProtocol::setErrorMsg(int val)
{
	switch (val) {
	   case 0: ErrorMsg = i18n("Archive is read-only"); break;
	}
	return FALSE;
}

//-------------------------------------------------------------------------------------------

const FileInfoList* ArchiveProtocol::entryInfoList(bool hidden, bool dirsOnly, bool showArchive)
{
	if (updateFlag && !updateEntries()) return 0;
	if (filterFlag && !updateFilteredEntries(hidden,dirsOnly,showArchive)) return 0;
	return entries;
}

const QStrList* ArchiveProtocol::entryList(bool hidden, bool dirsOnly, bool showArchive)
{
	if (updateFlag && !updateEntries()) return 0;
	if (filterFlag && !updateFilteredEntries(hidden,dirsOnly,showArchive)) return 0;
	return namedEntries;
}

bool ArchiveProtocol::mkdir(const char *pathname)
{
	return FALSE;
}

bool ArchiveProtocol::setPath(const char *pathname)
{
	CurrentPath = pathname;
	filterFlag = TRUE;
	return TRUE;
}

QString ArchiveProtocol::dirName()
{
	if (isRoot()) return "";
	int	pos = CurrentPath.findRev('/',2);
	return CurrentPath.mid(pos+1,CurrentPath.length()-pos);
}

bool ArchiveProtocol::remove(const char *filename)
{
	if (ReadOnly) return setErrorMsg(0);
	else return FALSE;
}

bool ArchiveProtocol::rmdir(const char *dirname, ProgressDlg *dlg)
{
	if (ReadOnly) return setErrorMsg(0);
	else return FALSE;
}

bool ArchiveProtocol::isRoot()
{ return (CurrentPath.isEmpty() || CurrentPath == "/");}

bool ArchiveProtocol::rename(const char *src, const char *dest)
{
	if (ReadOnly) return setErrorMsg(0);
	else return FALSE;
}

bool ArchiveProtocol::exists(const char *filename)
{ return TRUE;}

bool ArchiveProtocol::chmod(const char *filename, int perm)
{ return FALSE;}

bool ArchiveProtocol::copyToProtocol(const char *filename, const char *targetDir, bool move, ProgressDlg *dlg)
{
	if (ReadOnly) return setErrorMsg(0);
	else return FALSE;
}

bool ArchiveProtocol::copyFromProtocol(const char *filename, const char *targetDir, bool move, ProgressDlg *dlg)
{
	ProtocolCmd	*cmd = new ProtocolCmd(Prot_CopyFrom,this,(void*)filename,(void*)targetDir,(void*)move,(void*)dlg);
	launchThread(cmd);
	bool	retval = cmd->done && (bool)cmd->out;
	delete cmd;
	th_ID = 0;
	return retval;
}

bool ArchiveProtocol::thread_copyFromProtocol(const char *filename, const char *targetDir, bool move, ProgressDlg *dlg)
{
	if (ReadOnly && move) return setErrorMsg(0);
	char	*c = strrchr(filename,':');
	return extract(c+1,targetDir,dlg);
}

bool ArchiveProtocol::init()
{ return TRUE;}

bool ArchiveProtocol::finish()
{ return TRUE;}

bool ArchiveProtocol::cleanup()
{
	if (ProcID != 0) kill(ProcID,SIGKILL);
	if (fout) { CommDone(); fclose(fout); fout = 0;}
	return TRUE;
}

bool ArchiveProtocol::matchURL(KURL url)
{
	bool	retval = TRUE;
	if (!url.hasSubProtocol()) retval = FALSE;
	else if (url.parentURL() != File) retval = FALSE;
	return retval;
}

const char* ArchiveProtocol::currentURL()
{ return Prefix.data();}

bool ArchiveProtocol::isLocal()
{ return FALSE;}

uint ArchiveProtocol::freeSize(const char *dirname)
{ return 0;}

bool ArchiveProtocol::hasChilds(const char *dirname)
{ return TRUE;}

FileInfo* ArchiveProtocol::getInfo(KURL& url)
{ return 0;}
