/*
   filesystem/filesystem.cc
   This file is part of the Osirion project and is distributed under
   the terms of the GNU General Public License version 2
*/

#include <stdlib.h>

#include <list>

#ifdef _WIN32

#include <windows.h>
#include <shlobj.h>

#endif

#include "filesystem/filesystem.h"
#include "filesystem/diskfile.h"
#include "filesystem/file.h"
#include "sys/sys.h"

namespace filesystem
{

SearchPath filesystem_searchpath;
std::string filesystem_basename;
std::string filesystem_modname;

std::string filesystem_homedir;
std::string filesystem_writedir;
std::string filesystem_datadir;

std::string const & writedir()
{
	return filesystem_writedir;
}

std::string const & homedir()
{
	return filesystem_homedir;
}

SearchPath & searchpath()
{
	return filesystem_searchpath;
}
void init(const std::string &binaryname, const std::string & basename, const std::string & modname)
{
	con_print << "^BInitializing filesystem..." << std::endl;

	// clear everything
	filesystem_searchpath.clear();
	filesystem_basename.assign(basename);
	if (!filesystem_basename.size())
		filesystem_basename.assign("base");

	filesystem_modname.assign(modname);

#ifndef _WIN32
	// UNIX home directory is $HOME/.osirion
	filesystem_homedir.assign(getenv("HOME"));
	filesystem_homedir.append("/.osirion");

	// create homedir if necessary
	if (!sys::directory_exists(filesystem_homedir))
		sys::mkdir(filesystem_homedir);
	filesystem_homedir += '/';

#else
	// windows home directory is My Documents\My Games\Osirion
	char mydocuments[512];
	memset(mydocuments, 0, sizeof(mydocuments));

	SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocuments);
	filesystem_homedir.assign(mydocuments);

	if (filesystem_homedir.size()) {
		filesystem_homedir.append("\\My Games");
		if (!sys::directory_exists(filesystem_homedir))
			sys::mkdir(filesystem_homedir);

		filesystem_homedir.append("\\Osirion");
		if (!sys::directory_exists(filesystem_homedir))
			sys::mkdir(filesystem_homedir);
		filesystem_homedir.append("\\");

	} else {
		con_warn << "using fallback home directory" << std::endl;

		filesystem_homedir.assign("home");
		if (!sys::directory_exists(filesystem_homedir))
			sys::mkdir(filesystem_homedir);
		filesystem_homedir += '/';
	}
#endif

	// try the data/ subdirectory of the directory where the binary is located
	std::string current_datadir(binaryname);
	size_t i = current_datadir.size();
	while (--i && (current_datadir[i] != '/')) {
		current_datadir.erase(i, 1);
	}
	current_datadir.append("data/");

	// use the data/ subdirectory of the current working directory as a fallback
	if (!sys::directory_exists(current_datadir)) {
		current_datadir.assign("data/");
	}

	// the data dir set by the configure script
	std::string package_datadir(PACKAGE_DATADIR);
	std::string dir;

	// set writedir depending on modname and add home paths to the searchpath
	filesystem_writedir.assign(filesystem_homedir);
	if (filesystem_modname.size()) {
		// append modname to writedir
		filesystem_writedir.append(filesystem_modname);
	} else {
		// append basename to writedir
		filesystem_writedir.append(filesystem_basename);
	}

	// create writedir if necessary
	if (!sys::directory_exists(filesystem_writedir))
		sys::mkdir(filesystem_writedir);
	filesystem_writedir += '/';

	// modname search path
	if (filesystem_modname.size()) {
		// HOME/modname
		dir.assign(filesystem_homedir + filesystem_modname);
		if (sys::directory_exists(dir)) {
			dir += '/';
			filesystem_searchpath.push_back(dir);
		}

		// CURRENT/data/modname
		dir.assign(current_datadir + filesystem_modname);
		if (sys::directory_exists(dir)) {
			dir += '/';
			filesystem_searchpath.push_back(dir);
		}

		// PACKAGE_DATADIR/modname
		std::string dir(package_datadir + '/' + filesystem_modname);
		if (sys::directory_exists(dir)) {
			dir += '/';
			filesystem_searchpath.push_back(dir);
		}
	}

	// basename search path
	// HOME/basename
	dir.assign(filesystem_homedir + filesystem_basename);
	if (sys::directory_exists(dir)) {
		dir += '/';
		filesystem_searchpath.push_back(dir);
	}

	// PACKAGE_DATADIR/basename
	dir.assign(package_datadir + '/' + filesystem_basename);
	if (sys::directory_exists(dir)) {
		dir += '/';
		filesystem_searchpath.push_back(dir);
		filesystem_datadir.assign(dir);
	} else {
		// CURRENT/data/basename
		dir.assign(current_datadir + filesystem_basename);
		if (sys::directory_exists(dir)) {
			dir += '/';
			filesystem_searchpath.push_back(dir);
			filesystem_datadir.assign(dir);
		} else {
			con_warn << "data/" << basename << " not found!" << std::endl;
		}
	}

	// print search path
	for (SearchPath::iterator path = filesystem_searchpath.begin(); path != filesystem_searchpath.end(); ++path) {
#ifdef _WIN32
		for (size_t i = 0; i < (*path).size(); i++)
			if ((*path)[i] == '/')(*path)[i] = '\\';
#endif
		con_print << "  directory " << (*path) << std::endl;
	}

	// create writedir
	con_print << "  home " << filesystem_writedir << std::endl;
}

void shutdown()
{
	con_print << "^BShutting down filesystem..." << std::endl;

	filesystem_searchpath.clear();
	filesystem_basename.clear();

	filesystem_modname.clear();
	filesystem_homedir.clear();

	filesystem_writedir.clear();
}

File *open(const char *filename)
{
	// for now, File is always a DiskFile
	DiskFile *f = new DiskFile();
	if (!f->open(filename)) {
		delete f;
		f = 0;
		return 0;
	}
	return f;
}

void close(File *file)
{
	if (!file)
		return;

	file->close();
	delete file;
}

}