#include "filesystem.hpp"
#ifdef _WIN32
	#include <windows.h>
	#include <direct.h>
	#include <io.h>
	#define PATH_MAX MAX_PATH

	#define chdir _chdir
	#define getcwd _getcwd
	#define fileno _fileno
	#define isatty _isatty
#else
	#include <limits.h>
	#include <unistd.h>
#endif

void sprawl::filesystem::Chdir(String const& path)
{
	chdir(path.c_str());
}

sprawl::String sprawl::filesystem::GetCwd()
{
	char cwd[PATH_MAX];
	getcwd(cwd, PATH_MAX);
	return sprawl::String(cwd);
}

sprawl::filesystem::File sprawl::filesystem::Open(String const& path, char const* const mode)
{
	return File( fopen( path.c_str(), mode ), sprawl::String(mode) );
}


sprawl::filesystem::File::File(const sprawl::filesystem::File& other)
	: m_file(other.m_file)
	, m_mode(other.m_mode)
	, m_refCount(other.m_refCount)
{
	if(m_refCount)
	{
		++(*m_refCount);
	}
}

void sprawl::filesystem::File::operator=(const sprawl::filesystem::File& other)
{
	if(m_file)
	{
		if(--(*m_refCount) == 0)
		{
			fclose(m_file);
		}
	}
	m_file = other.m_file;
	m_mode = other.m_mode;
	m_refCount = other.m_refCount;
	if(m_refCount)
	{
		++(*m_refCount);
	}
}

sprawl::filesystem::File::~File()
{
	if(m_file)
	{
		if(--(*m_refCount) == 0)
		{
			fclose(m_file);
		}
	}
}

void sprawl::filesystem::File::Close()
{
	*m_refCount = 0;
	fclose(m_file);
	m_file = nullptr;
}

void sprawl::filesystem::File::Sync()
{
#ifdef _WIN32
	_commit(FileNo());
#else
	fsync(FileNo());
#endif
}

void sprawl::filesystem::File::Flush()
{
	fflush(m_file);
}

int sprawl::filesystem::File::FileNo()
{
	return fileno(m_file);
}

bool sprawl::filesystem::File::IsATTY()
{
	return isatty(FileNo());
}

sprawl::String sprawl::filesystem::File::Read(int numBytes)
{
	if(numBytes == -1)
	{
		numBytes = FileSize() - Tell();
	}
	char* buffer = (char*)malloc(numBytes);
	size_t size = fread(buffer, 1, numBytes, m_file);
	sprawl::String ret(buffer, size);
	free(buffer);
	return std::move(ret);
}

sprawl::String sprawl::filesystem::File::ReadLine(int numBytes)
{
	if(numBytes == -1)
	{
		numBytes = FileSize() - Tell();
	}
	numBytes += 1;
	char* buffer = (char*)malloc(numBytes);
	char* result = fgets(buffer, numBytes, m_file);
	sprawl::String ret(buffer);
	free(buffer);
	return std::move(ret);
}

void sprawl::filesystem::File::Seek(int offset, RelativeTo relativeTo)
{
	switch(relativeTo)
	{
		case RelativeTo::Beginning:
		{
			fseek(m_file, offset, SEEK_SET);
			return;
		}
		case RelativeTo::CurrentPosition:
		{
			fseek(m_file, offset, SEEK_CUR);
			return;
		}
		case RelativeTo::End:
		{
			fseek(m_file, offset, SEEK_END);
			return;
		}
	}
}

int sprawl::filesystem::File::Tell()
{
	return int(ftell(m_file));
}

void sprawl::filesystem::File::Truncate(int size)
{
	if(size == -1)
	{
		size = Tell();
	}
	if(Tell() > size)
	{
		Seek(size, RelativeTo::Beginning);
	}
#ifdef _WIN32
	_chsize(FileNo(), size);
#else
	ftruncate(FileNo(), size);
#endif
}

void sprawl::filesystem::File::Write(String const& str)
{
	fwrite(str.c_str(), 1, str.length(), m_file);
}

bool sprawl::filesystem::File::IsClosed()
{
	return *m_refCount <= 0;
}

sprawl::String const& sprawl::filesystem::File::Mode()
{
	return m_mode;
}

int sprawl::filesystem::File::FileSize()
{
	int current = Tell();
	Seek(0, RelativeTo::End);
	int size = Tell();
	Seek(current, RelativeTo::Beginning);
	return size;
}
