/*  $Id: directory.cpp,v 1.12 2004/03/09 15:36:12 sphair Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  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
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser 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
**
*/

#include "Core/precomp.h"
#include "API/Core/IOData/directory.h"
#include "API/Core/IOData/directory_scanner.h"

#ifndef WIN32
#include <unistd.h>
#include <stdio.h>
#ifndef MAX_PATH
#define MAX_PATH PATH_MAX
#endif
#else
#include <direct.h>
#ifndef chdir
#define _chdir chdir
#endif
#ifndef MAX_PATH
#define _MAX_PATH MAX_PATH
#endif
#endif

#ifdef __BORLANDC__
#include <dir.h>
#endif

#include <sys/stat.h>
#include <sys/types.h>

/////////////////////////////////////////////////////////////////////////////
// Operations

bool CL_Directory::create(const std::string &dir_name)
{
	if (dir_name.empty())
		return false;

	// this will be a full path
	std::string full_path; 	// calculate the full path

	#ifdef WIN32
		DWORD buff_len = ::GetFullPathName(dir_name.c_str(), 0, 0, 0);

		if (buff_len == 0)
			// can't calculate, return bad status
			return false;
		else
		{
			char * buffer = new char[buff_len + 1];
			char * buffer_ptr_to_filename = 0;
			// Obtaining full path
			buff_len = ::GetFullPathName(dir_name.c_str(), buff_len, buffer, &buffer_ptr_to_filename);
			if (buff_len == 0)
				// can't obtain full path, return bad status
				return false;
			else
				// ok, save it
				full_path = buffer;
		}
	#else
		// TODO: add here Linux version of GetFullPathName
		full_path = dir_name;
	#endif

#ifdef WIN32
		return ::CreateDirectory(full_path.c_str(), NULL) != 0;
#else
		return ::mkdir(full_path.c_str(), 0755) == 0;
#endif
}

bool CL_Directory::remove(const std::string &dir_name, bool delete_files, bool delete_sub_directories)
{
	if (dir_name.empty())
		return false;

	// this will be a full path
	std::string full_path;

	// calculate the full path
	#ifdef WIN32
		DWORD buff_len = ::GetFullPathName(dir_name.c_str(), 0, 0, 0);

		if (buff_len == 0)
			// can't calculate, return bad status
			return false;
		else
		{
			char * buffer = new char[buff_len + 1];
			char * buffer_ptr_to_filename = 0;
			// Obtaining full path
			buff_len = ::GetFullPathName(dir_name.c_str(), buff_len, buffer, &buffer_ptr_to_filename);
			if (buff_len == 0)
				// can't obtaing full path, return bad status
				return false;
			else
				// ok, save it
				full_path = buffer;
		}
	#else
		// TODO: add here Linux version of GetFullPathName
		full_path = dir_name;
	#endif

	// This scope needed for deleting directiory at end of function,
	// because scanner lock current dir :(
	{
		CL_DirectoryScanner scanner;

		if (!scanner.scan(full_path))
			// can't even start scaning
			return false;

		// FIXME: probably bug in directory_scanner, it return ""
		// for first file :(
		if (scanner.next())
		while(1)
		{
			// If found sub_directory, try remove it,
			// also checking for "." and "..", because they are unremovable
			if (scanner.is_directory() && delete_sub_directories &&
				scanner.get_name() != "." && scanner.get_name() != "..")
			{
				// FIXME: directory_scanner lock directory, so it can't be
				// removed, this is workaround
				std::string sub_dir_path = scanner.get_pathname();
				bool scann_successfull = scanner.next();

				// delete files in sub_directory
				if (!CL_Directory::remove(sub_dir_path.c_str(),
							  delete_files,
							  delete_sub_directories))
					return false;

				if (!scann_successfull)
					break;
				else
					continue;
			}
			else
			{
				// Check for deleting file (or whatever is not directory),
				// if this is allowed
				if (delete_files && !scanner.is_directory())
				{
					// delete a file
					#ifdef WIN32
						if (::DeleteFile(scanner.get_pathname().c_str()) == 0)
							return false;
					#else
						if (::remove(scanner.get_pathname().c_str()) != 0)
							return false;
					#endif
					if (!scanner.next())
						break;
				}
				// This is for "." and ".."
				else
				{
					if (!scanner.next())
						break;
				}
			}
		}
	}
	// Finaly remove the directory (or sub_directory if in recursion)
	#ifdef WIN32
		return ::RemoveDirectory(full_path.c_str()) != 0;
	#else
		return ::rmdir(full_path.c_str()) == 0;
	#endif
}

bool CL_Directory::change_to(const std::string &dir_name)
{
	return chdir(dir_name.c_str()) == 0;
}

std::string CL_Directory::get_current()
{
	char cwd_buffer[MAX_PATH];
	if (getcwd(cwd_buffer, MAX_PATH) == NULL)
		throw CL_Error("Working dir is more than legal length !");
	return cwd_buffer;
}
