// This file is part of NECSim project which is released under MIT license.
// See file **LICENSE.txt** or visit https://opensource.org/licenses/MIT) for full license details.

/**
 * @author Samuel Thompson
 * @date 19/07/2017
 * @file Filesystem.cpp
 *
 * @copyright <a href="https://opensource.org/licenses/MIT"> MIT Licence.</a>
 * @brief Contains routines for checking files and folder exist, opening sqlite databases safely, with support for various
 * virtual filesystems, and checking parents of a file exist.
 *
 * Contact: samuel.thompson14@imperial.ac.uk or thompsonsed@gmail.com
 */

#include <string>
#include <sstream>
#include <boost/filesystem.hpp>
#ifdef WIN_INSTALL
#include <windows.h>
#define sleep Sleep
#endif
#include "Filesystem.h"
#include "CustomExceptions.h"
#include "Logger.h"


void openSQLiteDatabase(const string &database_name, sqlite3 *&database)
{
	int rc;
	if(database_name == ":memory:")
	{
		rc = sqlite3_open(":memory:", &database);
		if(rc != SQLITE_OK && rc != SQLITE_DONE)
		{
			stringstream ss;
			ss << "Could not connect to in-memory database. Error: " << rc << endl;
			ss << " (" << sqlite3_errmsg(database) << ")" << endl;
			throw FatalException(ss.str());
		}
	}
#ifdef WIN_INSTALL
	rc = sqlite3_open_v2(database_name.c_str(), &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "win32");
#else
	rc = sqlite3_open_v2(database_name.c_str(), &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "unix-dotfile");
#endif
	if(rc != SQLITE_OK && rc != SQLITE_DONE)
	{
		int i = 0;
		while((rc != SQLITE_OK && rc != SQLITE_DONE) && i < 10)
		{
			i++;
			sleep(1);
			rc = sqlite3_open_v2(database_name.c_str(), &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
								 "unix-dotfile");
		}
		// Attempt different opening method if the first fails.
		int j = 0;
		while((rc != SQLITE_OK && rc != SQLITE_DONE) && j < 10)
		{
			j++;
			sleep(1);
			rc = sqlite3_open(database_name.c_str(), &database);
		}
		if(rc != SQLITE_OK && rc != SQLITE_DONE)
		{
			stringstream ss;
			ss << "ERROR_SQL_010: SQLite database file could not be opened. Check the folder exists and you "
				  "have write permissions. (REF1) Error code: "
			   << rc << endl;
			ss << " Attempted call " << max(i, j) << " times" << endl;
			throw FatalException(ss.str());
		}
	}
}

void createParent(string file)
{
	// Boost < 1.59 support
	boost::filesystem::path file_path(file);
	auto parent = file_path.parent_path().string();
	if (parent.length() > 0)
	{
		std::string::iterator it = parent.end() - 1;
		if (*it == '/')
		{
			parent.erase(it);
		}
		boost::filesystem::path parent_path(parent);
		if(!boost::filesystem::exists(parent_path))
		{
			if(!parent_path.empty())
			{
				if(!boost::filesystem::create_directories(parent_path))
				{
					throw runtime_error("Cannot create parent folder for " + file);
				}
			}
		}
	}
}

bool doesExist(string testfile)
{
	if(boost::filesystem::exists(testfile))
	{
		stringstream os;
		os << "\rChecking folder existance..." << testfile << " exists!               " << endl;
		writeInfo(os.str());
		return true;
	}
	else
	{
		throw runtime_error(string("ERROR_MAIN_008: FATAL. Input or output folder does not exist: " + testfile + "."));
	}
}

bool doesExistNull(string testfile)
{
	return testfile == "null" || testfile == "none" || doesExist(testfile);
}

unsigned long cantorPairing(unsigned long x1, unsigned long x2)
{
	return ((x1 + x2) * (x1 + x2 + 1) / 2) + x2;
}

vector<string> getCsvLineAndSplitIntoTokens(istream &str)
{
	vector<string> result;
	string line;
	getline(str, line);

	stringstream lineStream(line);
	string cell;

	while(getline(lineStream, cell, ','))
	{
		result.push_back(cell);
	}
	// This checks for a trailing comma with no data after it.
	if(!lineStream && cell.empty())
	{
		// If there was a trailing comma then add an empty element.
		result.emplace_back("");
	}
	return result;
}
