//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002 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/format/kmailMaildirFormat.hpp"

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

#include "vmime/platform.hpp"


namespace vmime {
namespace net {
namespace maildir {
namespace format {


kmailMaildirFormat::kmailMaildirFormat(const shared_ptr <context>& ctx)
	: maildirFormat(ctx) {

}


const string kmailMaildirFormat::getName() const {

	return "kmail";
}


void kmailMaildirFormat::createFolder(const folder::path& path) {

	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	if (!fsf->isValidPath(folderPathToFileSystemPath(path, ROOT_DIRECTORY))) {
		throw exceptions::invalid_folder_name();
	}

	shared_ptr <utility::file> rootDir = fsf->create(
		folderPathToFileSystemPath(path, ROOT_DIRECTORY)
	);

	shared_ptr <utility::file> newDir = fsf->create(
		folderPathToFileSystemPath(path, NEW_DIRECTORY)
	);
	shared_ptr <utility::file> tmpDir = fsf->create(
		folderPathToFileSystemPath(path, TMP_DIRECTORY)
	);
	shared_ptr <utility::file> curDir = fsf->create(
		folderPathToFileSystemPath(path, CUR_DIRECTORY)
	);

	rootDir->createDirectory(true);

	newDir->createDirectory(false);
	tmpDir->createDirectory(false);
	curDir->createDirectory(false);
}


void kmailMaildirFormat::destroyFolder(const folder::path& path) {

	// Delete 'folder' and '.folder.directory' directories
	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	maildirUtils::recursiveFSDelete(
		fsf->create(folderPathToFileSystemPath(path, ROOT_DIRECTORY))  // root
	);

	maildirUtils::recursiveFSDelete(
		fsf->create(folderPathToFileSystemPath(path, CONTAINER_DIRECTORY))  // container
	);
}


bool kmailMaildirFormat::folderExists(const folder::path& path) const {

	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	shared_ptr <utility::file> rootDir = fsf->create(
		folderPathToFileSystemPath(path, ROOT_DIRECTORY)
	);

	shared_ptr <utility::file> newDir = fsf->create(
		folderPathToFileSystemPath(path, NEW_DIRECTORY)
	);
	shared_ptr <utility::file> tmpDir = fsf->create(
		folderPathToFileSystemPath(path, TMP_DIRECTORY)
	);
	shared_ptr <utility::file> curDir = fsf->create(
		folderPathToFileSystemPath(path, CUR_DIRECTORY)
	);

	return rootDir->exists() && rootDir->isDirectory() &&
	       newDir->exists() && newDir->isDirectory() &&
	       tmpDir->exists() && tmpDir->isDirectory() &&
	       curDir->exists() && curDir->isDirectory();
}


const utility::file::path kmailMaildirFormat::folderPathToFileSystemPath(
	const folder::path& path,
	const DirectoryType type
) const {

	// Root path
	utility::file::path fsPath = getContext()->getStore()->getFileSystemPath();

	const size_t pathSize = path.getSize();
	const size_t count = (type == CONTAINER_DIRECTORY
		? pathSize : (pathSize >= 1 ? pathSize - 1 : 0));

	// Parent folders
	for (size_t i = 0 ; i < count ; ++i) {

		utility::file::path::component comp(path[i]);

		// TODO: may not work with all encodings...
		comp.setBuffer("." + comp.getBuffer() + ".directory");

		fsPath /= comp;
	}

	// Last component
	if (path.getSize() != 0 && type != CONTAINER_DIRECTORY) {

		fsPath /= path.getLastComponent();

		switch (type) {

			case ROOT_DIRECTORY:

				// Nothing to add
				break;

			case NEW_DIRECTORY:

				fsPath /= NEW_DIR;
				break;

			case CUR_DIRECTORY:

				fsPath /= CUR_DIR;
				break;

			case TMP_DIRECTORY:

				fsPath /= TMP_DIR;
				break;

			case CONTAINER_DIRECTORY:

				// Can't happen...
				break;
		}
	}

	return fsPath;
}


const std::vector <folder::path> kmailMaildirFormat::listFolders(
	const folder::path& root,
	const bool recursive
) const {

	std::vector <folder::path> list;
	listFoldersImpl(list, root, recursive);

	return list;
}


void kmailMaildirFormat::listFoldersImpl(
	std::vector <folder::path>& list,
	const folder::path& root,
	const bool recursive
) const {

	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	shared_ptr <utility::file> rootDir = fsf->create(
		folderPathToFileSystemPath(root, root.isEmpty() ? ROOT_DIRECTORY : CONTAINER_DIRECTORY)
	);

	if (rootDir->exists()) {

		shared_ptr <utility::fileIterator> it = rootDir->getFiles();

		while (it->hasMoreElements()) {

			shared_ptr <utility::file> file = it->nextElement();

			if (isSubfolderDirectory(*file)) {

				const utility::path subPath = root / file->getFullPath().getLastComponent();

				list.push_back(subPath);

				if (recursive) {
					listFoldersImpl(list, subPath, true);
				}
			}
		}

	} else {

		// No sub-folder
	}
}


// static
bool kmailMaildirFormat::isSubfolderDirectory(const utility::file& file) {

	// A directory which name does not start with '.' is listed as a sub-folder
	if (file.isDirectory() &&
	    file.getFullPath().getLastComponent().getBuffer().length() >= 1 &&
	    file.getFullPath().getLastComponent().getBuffer()[0] != '.') {

		return true;
	}

	return false;
}


void kmailMaildirFormat::renameFolder(const folder::path& oldPath, const folder::path& newPath) {

	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	shared_ptr <utility::file> rootDir = fsf->create(
		folderPathToFileSystemPath(oldPath, ROOT_DIRECTORY)
	);
	shared_ptr <utility::file> contDir = fsf->create(
		folderPathToFileSystemPath(oldPath, CONTAINER_DIRECTORY)
	);

	try {

		const utility::file::path newRootPath =
			folderPathToFileSystemPath(newPath, ROOT_DIRECTORY);
		const utility::file::path newContPath =
			folderPathToFileSystemPath(newPath, CONTAINER_DIRECTORY);

		rootDir->rename(newRootPath);

		// Container directory may not exist, so ignore error when trying to rename it
		try {
			contDir->rename(newContPath);
		} catch (exceptions::filesystem_exception& e) {
			// Ignore
		}

	} catch (exceptions::filesystem_exception& e) {

		// Revert to old location
		const utility::file::path rootPath =
			folderPathToFileSystemPath(oldPath, ROOT_DIRECTORY);
		const utility::file::path contPath =
			folderPathToFileSystemPath(oldPath, CONTAINER_DIRECTORY);

		try {
			rootDir->rename(rootPath);
			contDir->rename(contPath);
		} catch (exceptions::filesystem_exception& e) {
			// Ignore (not recoverable)
		}

		throw;
	}
}


bool kmailMaildirFormat::folderHasSubfolders(const folder::path& path) const {

	shared_ptr <utility::fileSystemFactory> fsf = platform::getHandler()->getFileSystemFactory();

	shared_ptr <utility::file> rootDir = fsf->create(
		folderPathToFileSystemPath(path, CONTAINER_DIRECTORY)
	);

	shared_ptr <utility::fileIterator> it = rootDir->getFiles();

	while (it->hasMoreElements()) {

		shared_ptr <utility::file> file = it->nextElement();

		if (isSubfolderDirectory(*file)) {
			return true;
		}
	}

	return false;
}


bool kmailMaildirFormat::supports() const {

	// This is the default
	return true;
}


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


#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_MESSAGING_PROTO_MAILDIR

