/*
    PIP - Platform Independent Primitives
    Directory
    Copyright (C) 2014  Ivan Pelipenko peri4ko@gmail.com

    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, see <http://www.gnu.org/licenses/>.
*/

#include "pidir.h"

#if !defined(ANDROID)
#ifdef WINDOWS
	const PIChar PIDir::separator = '\\';
#else
	const PIChar PIDir::separator = '/';
#endif


PIDir::PIDir(const PIString & dir) {
	path_ = dir;
}


PIDir::PIDir(const PIFile & file) {
	path_ = file.path();
	if (isExists()) return;
	int pos = path_.findLast(separator);
	path_.cutRight(path_.size_s() - pos);
}


bool PIDir::operator ==(const PIDir & d) const {
	return d.absolutePath() == absolutePath();
}


bool PIDir::isAbsolute() const {
	if (path_.size() == 0) return false;
#ifdef WINDOWS
	if (path_.size() < 3) return false;
	return (path_[1] == ":");
#else
	return (path_[0] == separator);
#endif
}


PIString PIDir::absolutePath() const {
	if (isAbsolute()) return path_;
	PIDir d(path_);
	d.setDir(PIDir::current().path_ + separator + path_);
	return d.path_;
}


PIDir & PIDir::cleanPath() {
	PIString p(path_);
	if (p.size() == 0) {
		path_ = ".";
		return *this;
	}
	path_.replaceAll(PIString(separator) + PIString(separator), PIString(separator));
	bool is_abs = isAbsolute();
	PIStringList l = PIString(p).split(separator);
#ifdef WINDOWS
	PIString letter;
	if (is_abs) letter = l.take_front();
#endif
	l.removeAll(".");
	l.removeAll("");
	bool found = true;
	while (found) {
		found = false;
		for (uint i = 0; i < l.size() - 1; ++i) {
			if (l.size() < 2) break;
			if (l[i] != ".." && l[i + 1] == "..") {
				if (l.size() + i > 2) l.remove(i, 2);
				else l.remove(i, 1);
				--i;
				found = true;
			}
		}
	}
	found = true;
	while (found) {
		found = false;
		if (l.size() > 0) if (l[0] == "..")
			{l.pop_front(); found = true;}
	}
	path_ = separator + l.join(separator); /// TODO think about windows
	if (!is_abs) path_.prepend(".");
#ifdef WINDOWS
	else path_.prepend(letter);
#endif
	if (path_.size() == 0) path_ = ".";
	return *this;
}


PIDir & PIDir::setDir(const PIString & path) {
	path_ = path;
	cleanPath();
	return *this;
}


PIDir & PIDir::cd(const PIString & path) {
	if (path_.size() == 0) return *this;
	if (path_[path_.size() - 1] != separator) path_ += separator;
	path_ += path;
	return cleanPath();
}


bool PIDir::make(bool withParents) {
	PIDir d = cleanedPath();
	PIString tp;
	bool is_abs = isAbsolute();
	if (withParents) {
		PIStringList l = d.path_.split(separator);
		for (int i = l.size_s() - 1; i >= 0; --i) {
			if (i > 1) tp = PIStringList(l).remove(i, l.size_s() - i).join(separator);
			else {
				tp = separator;
				if (!is_abs) tp.push_front('.');
			}
			//cout << tp << endl;
			if (isExists(tp)) {
				for (int j = i + 1; j <= l.size_s(); ++j) {
					tp = PIStringList(l).remove(j, l.size_s() - j).join(separator);
					//cout << tp << endl;
					if (makeDir(tp)) continue;
					else return false;
				}
				break;
			};
		}
	} else
		if (makeDir(d.path_)) return true;
	return false;
}


PIVector<PIDir::DirEntry> PIDir::entries() {
	PIVector<DirEntry> l;
	PIString p(cleanedPath().path_);
#ifdef WINDOWS
	WIN32_FIND_DATA fd; memset(&fd, 0, sizeof(fd));
	p += "\\*";
	void * hf = FindFirstFile((LPCTSTR)(p.data()), &fd);
	if (!hf) return l;
	LARGE_INTEGER filesize;
	do {
		int m = 0;
		filesize.LowPart = filesize.HighPart = 0;
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) m |= S_IFHDN;
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) m |= S_IFBLK;
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) m |= S_IFDIR;
		else {
			m |= S_IFREG;
			filesize.LowPart = fd.nFileSizeLow;
			filesize.HighPart = fd.nFileSizeHigh;
		}
		l << DirEntry(fd.cFileName, m, filesize.QuadPart);
		memset(&fd, 0, sizeof(fd));
	} while (FindNextFile(hf, &fd) != 0);
	FindClose(hf);
#else
	dirent ** list;
#  ifndef QNX
	int cnt = scandir(p.data(), &list, 0, versionsort);
#  else
	int cnt = scandir(const_cast<char*>(p.data()), 0, 0, versionsort);
#  endif
	struct stat fs;
	for (int i = 0; i < cnt; ++i) {
		stat((p + separator + PIString(list[i]->d_name)).data(), &fs);
		l << DirEntry(list[i]->d_name, fs.st_mode, fs.st_size);
		if (list[i]->d_name[0] == '.') l.back().mode |= S_IFHDN;
		delete list[i];
	}
	delete list;
#endif
	return l;
}



bool PIDir::isExists(const PIString & path) {
#ifdef WINDOWS
	return (GetFileAttributes((LPCTSTR)(path.data())) & FILE_ATTRIBUTE_DIRECTORY);
#else
	DIR * dir_ = opendir(path.data());
	if (dir_ == 0) return false;
	closedir(dir_);
#endif
	return true;
}


PIDir PIDir::current() {
	char rc[1024];
#ifdef WINDOWS
	memset(rc, 0, 1024);
	if (GetCurrentDirectory(1024, (LPTSTR)rc) == 0) return PIString();
#else
	if (getcwd(rc, 1024) == 0) return PIString();
#endif
	return PIDir(rc);
}


PIDir PIDir::home() {
	char * rc = 0;
#ifdef WINDOWS
	rc = new char[1024];
	memset(rc, 0, 1024);
	if (ExpandEnvironmentStrings((LPCTSTR)"%HOMEPATH%", (LPTSTR)rc, 1024) == 0) {
		delete[] rc;
		return PIDir();
	}
	PIString s(rc);
	delete[] rc;
	return PIDir(s);
#else
	rc = getenv("HOME");
	if (rc == 0) return PIDir();
	return PIDir(rc);
#endif
}


PIDir PIDir::temporary() {
	char * rc = 0;
#ifdef WINDOWS
	rc = new char[1024];
	memset(rc, 0, 1024);
	int ret = GetTempPath(1024, (LPTSTR)rc);
	if (ret == 0) {
		delete[] rc;
		return PIDir();
	}
	PIString s(rc);
	delete[] rc;
	return PIDir(s);
#else
	rc = tmpnam(0);
	if (rc == 0) return PIDir();
	PIString s(rc);
	return PIDir(s.left(s.findLast(PIDir::separator)));
#endif
}


bool PIDir::make(const PIString & path, bool withParents) {
	PIDir d(path);
	if (d.isExists()) return true;
	return d.make(withParents);
}


bool PIDir::setCurrent(const PIString & path) {
#ifdef WINDOWS
	if (SetCurrentDirectory((LPCTSTR)(path.data())) != 0) return true;
#else
	if (chdir(path.data()) == 0) return true;
#endif
	printf("[PIDir] setCurrent(\"%s\") error: %s\n", path.data(), errorString().c_str());
	return false;
}


bool PIDir::makeDir(const PIString & path) {
#ifdef WINDOWS
	if (CreateDirectory((LPCTSTR)(path.data()), NULL) != 0) return true;
#else
	if (mkdir(path.data(), 16877) == 0) return true;
#endif
	printf("[PIDir] makeDir(\"%s\") error: %s\n", path.data(), errorString().c_str());
	return false;
}


bool PIDir::removeDir(const PIString & path) {
#ifdef WINDOWS
	if (RemoveDirectory((LPCTSTR)(path.data())) != 0) return true;
#else
	if (rmdir(path.data()) == 0) return true;
#endif
	printf("[PIDir] removeDir(\"%s\") error: %s\n", path.data(), errorString().c_str());
	return false;
}


bool PIDir::renameDir(const PIString & path, const PIString & new_name) {
	if (::rename(path.data(), new_name.data()) == 0) return true;
	printf("[PIDir] renameDir(\"%s\", \"%s\") error: %s\n", path.data(), new_name.data(), errorString().c_str());
	return false;
}


#endif
