#ifndef UTIL_H
#define UTIL_H

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

namespace util
{
using namespace std;

namespace internal
{
extern const char * error_prefix;
}

/************************************************************************/
/* Comparator for case-insensitive comparison in STL assos. containers  */
/* From Abhay:                                                          */
/* http://stackoverflow.com/questions/1801892/making-mapfind-operation-case-insensitive */
/************************************************************************/
struct ci_less : std::binary_function<std::string, std::string, bool>
{
	// case-independent (ci) compare_less binary function
	struct nocase_compare : public std::binary_function<unsigned char,unsigned char,bool> 
	{
		bool operator() (const unsigned char& c1, const unsigned char& c2) const
		{
			return tolower (c1) < tolower (c2);
		}
	};
	bool operator() (const std::string & s1, const std::string & s2) const
	{
		return std::lexicographical_compare
			(s1.begin (), s1.end (),   // source range
			s2.begin (), s2.end (),   // dest range
			nocase_compare ());  // comparison
	}
};



/**
*  Here we create a useful and easily understanable alias for the map.
**/
typedef map<string, vector<string>, ci_less> FileMap;
typedef map<string, bool, ci_less> OptionsMap;



// EXEC()
int8_t exec(int redirect_fd, const char * command,
            const char * arg1 = NULL,
            const char * arg2 = NULL,
            const char * arg3 = NULL,
            const char * arg4 = NULL,
            const char * arg5 = NULL,
            const char * arg6 = NULL);

inline
int8_t exec(const char * command,
            const char * arg1 = NULL,
            const char * arg2 = NULL,
            const char * arg3 = NULL,
            const char * arg4 = NULL,
            const char * arg5 = NULL,
            const char * arg6 = NULL)
{ return exec(STDOUT_FILENO, command, arg1, arg2, arg3, arg4, arg5, arg6); }


// FILESYSTEM FUNCTIONS
void assertExists(const string & path, int exit_code = -1);
bool exists(const string & path);
mode_t permissions(const string & path);
void forceRemoveDir(string dir);
string getcwdstr();

void copyFile(const string & source, const string & dest);
void copyFiles(const string & sourceFolder, const string & destFolder, const vector<string> & files);
void protectFiles(const string & folder, const vector<string> & files);
void protectDir(const string & dir);
void linkDirs(const string & sourceFolder, const string & destFolder, const vector<string> & dirs);
vector<string> get_files_in_dir(const string & dir);
bool is_symlink(const string & file);
string get_symlink_target(const string & symlink);

// STRING REPLACEMENT
bool   replaceFirst(string & str, const string & toreplace, const string & with);
size_t replaceAll  (string & str, const string & toreplace, const string & with);
size_t replaceAllInternal(string & str, const string & toreplace, const string & with);
size_t findNthLast(const string & str, char c, size_t n);
vector<string> tokenize(const string & str, char delim);


// IO OPERATIONS
string read_string_from_FILE(FILE * file, size_t max_length = -1);
void write_string_to_FILE(FILE * file, const char * str);
ssize_t writeBytesToFile(signed int fileDescriptor, const char * buffer, unsigned int bufferLength);
ssize_t writen(int fd, const void *vptr, size_t n);
ssize_t write(int fd, const string & str);
ssize_t write(int fd, int  val);
ssize_t write(int fd, long val);
ssize_t readn(int fd, void *vptr, size_t n);
ssize_t read(int fd, int  & val);
ssize_t read(int fd, long & val);


// STRING TYPE OPERATIONS
template <typename T>
string to_string(const T & value);
int intlen(unsigned int a);
void makeLower(string & str);
string toLower(const string & str);


// CONFIGURATION
void readConfig(const string & testsFolder, FileMap & map, const string & discriminator = "");
void readFile(const string & file, vector<string> & lines);
string readFile(const string & filename);
void readFileGeneric(const string & file, FileMap * map, vector<string> * lines, const string & discriminator = "");
char * processOptions(int argc, char ** argv, OptionsMap & opts, vector<string> & args);


// AUTOGRADER


// STUDENT CODE COMPILATION FUNCTIONS
void rename_main(const string & file, const string & newname);


// MACROS
void SET_ERROR_MESSAGE(const char * message);

#define STRINGIFY1(p)   #p
#define STR(p)          STRINGIFY1(p)

namespace internal
{
template<typename StrType>
void exit_if_error_output(const char * file, int32_t line, StrType message);
}

#define EXIT_IF_ERROR2(statement_check, message)                       \
	do {                                                               \
		errno = 0;                                                     \
		if ((statement_check) || errno != 0)                                         \
			util::internal::exit_if_error_output(__FILE__, __LINE__, message); \
	} while (0)

#define EXIT_IF_ERROR1(statement_check)                                \
	EXIT_IF_ERROR2(statement_check, #statement_check)

// Crazy hack for overloading!
// Arg counting from:
// http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/
// Overloading tips:
// http://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
#define EXIT_IF_ERROR_THIRD_ARG(a, b, c, ...) c

#define EXIT_IF_ERROR(...)                                    \
	EXIT_IF_ERROR_THIRD_ARG(__VA_ARGS__,                      \
	                        EXIT_IF_ERROR2,                   \
							EXIT_IF_ERROR1, 0) (__VA_ARGS__)


// INLINE IMPLEMENTATIONS
// Originally by radu
// http://notfaq.wordpress.com/2006/08/30/c-convert-int-to-string/
template <typename T>
inline string to_string(const T & value)
{
	stringstream ss;
	ss << value;
	return ss.str();
}

inline int intlen(unsigned int a)
{
	int len = 1;
	a /= 10;

	while (a != 0)
	{
		a = a/10;
		len++;
	}
	return len;
}

inline ssize_t write(int fd, const string & str)
{
	return writen(fd, str.c_str(), str.length()+1);
}

inline ssize_t write(int fd, int val)
{
	return writen(fd, &val, sizeof val);
}

inline ssize_t write(int fd, long val)
{
	return writen(fd, &val, sizeof val);
}

inline ssize_t read(int fd, int & val)
{
	return readn(fd, &val, sizeof val);
}

inline ssize_t read(int fd, long & val)
{
	return readn(fd, &val, sizeof val);
}

template <typename T, typename C, typename BinaryOp>
T accumulate(const C & collect, BinaryOp op, T init)
{
	typename C::const_iterator it = collect.begin();
	typename C::const_iterator end = collect.end();
	while (it != end)
		init = init + *it++;
	return init;
}

#if 0
template <typename C>
class collection_view<C>
{
	public:
	class iterator
	{
		C::iterator it;
		public:


}

template <typename T, C>
Collection<T> transform(C collection, UnaryOperator op)
#endif

} // namespace util

#endif