//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// 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 3 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.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library.  Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//

#include "vmime/config.hpp"


#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR


#include "vmime/net/maildir/maildirUtils.hpp"
#include "vmime/net/maildir/maildirStore.hpp"

#include "vmime/utility/random.hpp"
#include "vmime/platform.hpp"

#include "vmime/exception.hpp"


namespace vmime {
namespace net {
namespace maildir {


bool maildirUtils::isMessageFile(const utility::file& file)
{
	// Ignore files which name begins with '.'
	if (file.isFile() &&
	    file.getFullPath().getLastComponent().getBuffer().length() >= 1 &&
	    file.getFullPath().getLastComponent().getBuffer()[0] != '.')
	{
		return (true);
	}

	return (false);
}


// NOTE ABOUT ID/FLAGS SEPARATOR
// -----------------------------
// In the maildir specification, the character ':' is used to separate
// the unique identifier and the message flags.
//
// On Windows (and particularly FAT file systems), ':' is not allowed
// in a filename, so we use a dash ('-') instead. This is the solution
// used by Mutt/Win32, so we also use it here.
//
// To be compatible between implementations, we check for both
// characters when reading file names.


const utility::file::path::component maildirUtils::extractId
	(const utility::file::path::component& filename)
{
	size_t sep = filename.getBuffer().rfind(':');  // try colon

	if (sep == string::npos)
	{
		sep = filename.getBuffer().rfind('-');  // try dash (Windows)
		if (sep == string::npos) return (filename);
	}

	return (utility::path::component
		(string(filename.getBuffer().begin(), filename.getBuffer().begin() + sep)));
}


int maildirUtils::extractFlags(const utility::file::path::component& comp)
{
	size_t sep = comp.getBuffer().rfind(':');  // try colon

	if (sep == string::npos)
	{
		sep = comp.getBuffer().rfind('-');  // try dash (Windows)
		if (sep == string::npos) return 0;
	}

	const string flagsString(comp.getBuffer().begin() + sep + 1, comp.getBuffer().end());
	const size_t count = flagsString.length();

	int flags = 0;

	for (size_t i = 0 ; i < count ; ++i)
	{
		switch (flagsString[i])
		{
		case 'R': case 'r': flags |= message::FLAG_REPLIED; break;
		case 'S': case 's': flags |= message::FLAG_SEEN; break;
		case 'T': case 't': flags |= message::FLAG_DELETED; break;
		case 'F': case 'f': flags |= message::FLAG_MARKED; break;
		case 'P': case 'p': flags |= message::FLAG_PASSED; break;
		case 'D': case 'd': flags |= message::FLAG_DRAFT; break;
		}
	}

	return (flags);
}


const utility::file::path::component maildirUtils::buildFlags(const int flags)
{
	string str;
	str.reserve(8);

	str += "2,";

	if (flags & message::FLAG_MARKED)  str += "F";
	if (flags & message::FLAG_PASSED)  str += "P";
	if (flags & message::FLAG_REPLIED) str += "R";
	if (flags & message::FLAG_SEEN)    str += "S";
	if (flags & message::FLAG_DELETED) str += "T";
	if (flags & message::FLAG_DRAFT)   str += "D";

	return (utility::file::path::component(str));
}


const utility::file::path::component maildirUtils::buildFilename
	(const utility::file::path::component& id, const int flags)
{
	if (flags == message::FLAG_RECENT)
		return id;
	else
		return (buildFilename(id, buildFlags(flags)));
}


const utility::file::path::component maildirUtils::buildFilename
	(const utility::file::path::component& id,
	 const utility::file::path::component& flags)
{
#if VMIME_PLATFORM_IS_WINDOWS
	static const char DELIMITER[] = "-";
#else
	static const char DELIMITER[] = ":";
#endif

	return utility::path::component(id.getBuffer() + DELIMITER + flags.getBuffer());
}


const utility::file::path::component maildirUtils::generateId()
{
	std::ostringstream oss;
	oss.imbue(std::locale::classic());

	oss << utility::random::getTime();
	oss << ".";
	oss << utility::random::getProcess();
	oss << ".";
	oss << utility::random::getString(6);
	oss << ".";
	oss << platform::getHandler()->getHostName();

	return (utility::file::path::component(oss.str()));
}


void maildirUtils::recursiveFSDelete(shared_ptr <utility::file> dir)
{
	shared_ptr <utility::fileIterator> files = dir->getFiles();

	// First, delete files and subdirectories in this directory
	while (files->hasMoreElements())
	{
		shared_ptr <utility::file> file = files->nextElement();

		if (file->isDirectory())
		{
			maildirUtils::recursiveFSDelete(file);
		}
		else
		{
			try
			{
				file->remove();
			}
			catch (exceptions::filesystem_exception&)
			{
				// Ignore
			}
		}
	}

	// Then, delete this (empty) directory
	try
	{
		dir->remove();
	}
	catch (exceptions::filesystem_exception&)
	{
		// Ignore
	}
}



class maildirMessageSetEnumerator : public messageSetEnumerator
{
public:

	void enumerateNumberMessageRange(const vmime::net::numberMessageRange& range)
	{
		for (int i = range.getFirst(), last = range.getLast() ; i <= last ; ++i)
			list.push_back(i);
	}

	void enumerateUIDMessageRange(const vmime::net::UIDMessageRange& /* range */)
	{
		// Not supported
	}

public:

	std::vector <int> list;
};


// static
const std::vector <int> maildirUtils::messageSetToNumberList(const messageSet& msgs)
{
	maildirMessageSetEnumerator en;
	msgs.enumerate(en);

	return en.list;
}



//
// messageIdComparator
//

maildirUtils::messageIdComparator::messageIdComparator
	(const utility::file::path::component& comp)
	: m_comp(maildirUtils::extractId(comp))
{
}


bool maildirUtils::messageIdComparator::operator()
	(const utility::file::path::component& other) const
{
	return (m_comp == maildirUtils::extractId(other));
}


} // maildir
} // net
} // vmime


#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR

