//
// File.cpp
//
// Library: Foundation
// Package: Filesystem
// Module:  File
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/File.h"
#include "Poco/Path.h"
#include "Poco/DirectoryIterator.h"


#if defined(POCO_OS_FAMILY_WINDOWS) && defined(POCO_WIN32_UTF8)
#if defined(_WIN32_WCE)
#include "File_WINCE.cpp"
#else
#include "File_WIN32U.cpp"
#endif
#elif defined(POCO_OS_FAMILY_WINDOWS)
#include "File_WIN32.cpp"
#elif defined(POCO_VXWORKS)
#include "File_VX.cpp"
#elif defined(POCO_OS_FAMILY_UNIX)
#include "File_UNIX.cpp"
#endif
#include "Poco/Thread.h"


namespace Poco {


File::File()
{
}


File::File(const std::string& rPath): FileImpl(rPath)
{
}


File::File(const char* pPath): FileImpl(std::string(pPath))
{
}


File::File(const Path& rPath): FileImpl(rPath.toString())
{
}


File::File(const File& file): FileImpl(file.getPathImpl())
{
}


File::~File()
{
}


File& File::operator = (const File& file)
{
	setPathImpl(file.getPathImpl());
	return *this;
}


File& File::operator = (const std::string& rPath)
{
	setPathImpl(rPath);
	return *this;
}


File& File::operator = (const char* pPath)
{
	poco_check_ptr (pPath);
	setPathImpl(pPath);
	return *this;
}


File& File::operator = (const Path& rPath)
{
	setPathImpl(rPath.toString());
	return *this;
}


void File::swap(File& file)
{
	swapImpl(file);
}


bool File::exists() const
{
	return existsImpl();
}


bool File::canRead() const
{
	return canReadImpl();
}


bool File::canWrite() const
{
	return canWriteImpl();
}


bool File::canExecute() const
{
	return canExecuteImpl();
}


bool File::isFile() const
{
	return isFileImpl();
}


bool File::isDirectory() const
{
	return isDirectoryImpl();
}


bool File::isLink() const
{
	return isLinkImpl();
}


bool File::isDevice() const
{
	return isDeviceImpl();
}


bool File::isHidden() const
{
	return isHiddenImpl();
}


Timestamp File::created() const
{
	return createdImpl();
}


Timestamp File::getLastModified() const
{
	return getLastModifiedImpl();
}


File& File::setLastModified(const Timestamp& ts)
{
	setLastModifiedImpl(ts);
	return *this;
}


File::FileSize File::getSize() const
{
	return getSizeImpl();
}


File& File::setSize(FileSizeImpl size)
{
	setSizeImpl(size);
	return *this;
}


File& File::setWriteable(bool flag)
{
	setWriteableImpl(flag);
	return *this;
}


File& File::setReadOnly(bool flag)
{
	setWriteableImpl(!flag);
	return *this;
}


File& File::setExecutable(bool flag)
{
	setExecutableImpl(flag);
	return *this;
}

	
void File::copyTo(const std::string& rPath) const
{
	Path src(getPathImpl());
	Path dest(rPath);
	File destFile(rPath);
	if ((destFile.exists() && destFile.isDirectory()) || dest.isDirectory())
	{
		dest.makeDirectory();
		dest.setFileName(src.getFileName());
	}
	if (isDirectory())
		copyDirectory(dest.toString());
	else
		copyToImpl(dest.toString());
}


void File::copyDirectory(const std::string& rPath) const
{
	File target(rPath);
	target.createDirectories();

	Path src(getPathImpl());
	src.makeFile();
	DirectoryIterator it(src);
	DirectoryIterator end;
	for (; it != end; ++it)
	{
		it->copyTo(rPath);
	}
}


void File::moveTo(const std::string& rPath)
{
	copyTo(rPath);
	remove(true);
	setPathImpl(rPath);
}

	
void File::renameTo(const std::string& rPath)
{
	renameToImpl(rPath);
	setPathImpl(rPath);
}


void File::linkTo(const std::string& path, LinkType type) const
{
	linkToImpl(path, type);
}


void File::remove(bool recursive)
{
	if (recursive && !isLink() && isDirectory())
	{
		std::vector<File> files;
		list(files);
		for (std::vector<File>::iterator it = files.begin(); it != files.end(); ++it)
		{
			it->remove(true);
		}

		// Note: On Windows, removing a directory may not succeed at first
		// try because deleting files is not a synchronous operation. Files
		// are merely marked as deleted, and actually removed at a later time.
		//
		// An alternate strategy would be moving files to a different directory
		// first (on the same drive, but outside the deleted tree), and marking
		// them as hidden, before deleting them, but this could lead to other issues.
		// So we simply retry after some time until we succeed, or give up.

		int retry = 8;
		long sleep = 10;
		while (retry > 0)
		{
			try
			{
				removeImpl();
				retry = 0;
			}
			catch (DirectoryNotEmptyException&)
			{
				if (--retry == 0) throw;
				Poco::Thread::sleep(sleep);
				sleep *= 2;
			}
		}
	}
	else
	{
		removeImpl();
	}
}


bool File::createFile()
{
	return createFileImpl();
}


bool File::createDirectory()
{
	return createDirectoryImpl();
}


void File::createDirectories()
{
	if (!exists())
	{
		Path p(getPathImpl());
		p.makeDirectory();
		if (p.depth() > 1)
		{
			p.makeParent();
			File f(p);
			f.createDirectories();
		}
		try
		{
			createDirectoryImpl();
		}
		catch (FileExistsException&)
		{
		}
	}
}


void File::list(std::vector<std::string>& files) const
{
	files.clear();
	DirectoryIterator it(*this);
	DirectoryIterator end;
	while (it != end)
	{
		files.push_back(it.name());
		++it;
	}
}


File::FileSize File::totalSpace() const
{
	return totalSpaceImpl();
}


File::FileSize File::usableSpace() const
{
	return usableSpaceImpl();
}


File::FileSize File::freeSpace() const
{
	return freeSpaceImpl();
}


void File::list(std::vector<File>& files) const
{
	files.clear();
	DirectoryIterator it(*this);
	DirectoryIterator end;
	while (it != end)
	{
		files.push_back(*it);
		++it;
	}
}


void File::handleLastError(const std::string& path)
{
	handleLastErrorImpl(path);
}


} // namespace Poco