#include "includes.h"

class SuperString {
	private:
		string* _str;

		list<string>* splitted;
		static string result;
	public:
		SuperString(const char* str) {
			_str = new string(str);
			splitted = new list<string>();
		}
		~SuperString() {
			delete _str;
			delete splitted;
		}

		bool starts_with(string str) {
			if (str.length()>_str->length()) return false;
			if (_str->find(str)==0) return true;
			return false;
		}
		bool ends_with(string str) {
			if (str.length()>_str->length()) return false;
			if (_str->find(str)==_str->length()-str.length()) return true;
			return false;
		}
		
		int find(string str) {
			return _str->find(str);
		}
		bool contains(string str) {
			if (_str->find(str)!=string::npos) return true;
			return false;
		}
		SuperString& join(string str) {
			*_str += str;
			return *this;
		}
		/*SuperString& replace(std::string find, std::string replace) {
			size_t uPos = 0;
			size_t uFindLen = find.length();
			size_t uReplaceLen = replace.length();
			if (uFindLen==0) { return; }
			for ( ; (uPos = _str.find( find, uPos ))!=string::npos; ) {
				_str.replace(uPos, uFindLen, replace);
				uPos += uReplaceLen;
			}
			return *this;
		}*/
		list<string>& split(string str) {
			splitted->clear();

			string temp_main_string = *_str;
			unsigned long cutAt;
			while ( (cutAt = temp_main_string.find_first_of(str)) != temp_main_string.npos ) {
				if (cutAt>0) {
					splitted->push_back(temp_main_string.substr(0,cutAt));
				}
				else if (cutAt==0) splitted->push_back("");
				temp_main_string = temp_main_string.substr(cutAt+1);
			}
			if (temp_main_string.length()>0) {
				splitted->push_back(temp_main_string);
			}

			return *splitted;
		}
		void trim(void) {
			if (_str->length()==0) return;
			while (true) {
				char first_char = (*_str)[0];
				if      ( first_char == ' ' );
				else if ( first_char == '\t' );
				else break;
				_str->erase(0,1);
			}
			while (true) {
				unsigned long l = _str->length();
				char last_char = (*_str)[l-1];
				if      ( last_char == ' ' );
				else if ( last_char == '\t' );
				else break;
				_str->erase(l-2,l-1);
			}
		}

		const char* c_str() {
			return _str->c_str();
		}
		string& str() {
			return *_str;
		}

		int size() {
			return _str->size();
		}

		template <typename type_num> static string& to_string(type_num num) {
			stringstream s;
			s << num;
			result = s.str();
			return result;
		}
		template <typename type_num> static type_num to_number(string str) {
			type_num num;
			to_number(str,num);
			return num;
		}
		template <typename type_num> static bool to_number(string str, type_num* number) {
			if (str.length()==0) return false;
			*number = 0;
			bool negate = false;
			int decimal_loc = -1;
			for (unsigned int i=0; i<str.length(); ++i) {
				char c = str[i];
				if (c<='9'&&c>='0') {
					*number *= 10;
					*number += c-'0';
				}
				else if (c=='-'&&i==0) {
					negate = true;
				}
				else if (c=='.') {
					if (decimal_loc==-1) decimal_loc = str.length()-i-1;
					else return false;
				}
				else return false;
			}
			if (decimal_loc!=-1) {
				for (int i=0;i<decimal_loc;++i) *number /= (type_num)(10);
			}
			if (negate) {
				*number = -*number;
			}
			return true;
		}
	private:
};
string SuperString::result;

class FileReader {
	private:
		list<SuperString> lines;
		char* _file;
		SuperString* _file_string;
	private:
		void normalize_newlines(char* source) {
			unsigned long index_read = 0;
			unsigned long index_write = 0;
			while (true) {
				if (source[index_read]=='\r') {
					source[index_read] = '\n';
					if (source[index_read+1]=='\n') index_read++;
				}
				else if (source[index_read]=='\0') break;
				source[index_write] = source[index_read];
				index_read++;
				index_write++;
			}
			source[index_write] = '\0';
		}
		char* file_get_file(const char* path, bool normalize=true) {
			//Open the file.
			FILE* file = fopen(path,"rb");
			if (file==NULL) {
				return NULL;
			}
		
			//Find the end of the file
			fseek(file,0,SEEK_END);
			long file_size = ftell(file) / sizeof(char);
			rewind(file);

			//This will be more than we need for multi-line files, as fread ignores
			//some whitespace line terminators.  The +1 is for the '\0' character, and
			//is necessary only if there are no line separators in the file.
			char* buffer = (char*)malloc(sizeof(char)*(file_size+1));

			//Copy the file into the buffer:
			size_t result = fread(buffer,1,file_size,file);

			fclose(file);

			buffer[result] = '\0';

			if (normalize) normalize_newlines(buffer);

			return buffer;
		}
	public:
		FileReader(const char* path) {
			_file = file_get_file(path);
			if (_file==NULL) throw runtime_error("Could not open file!");

			_file_string = new SuperString(_file);
		}
		~FileReader() {
			free(_file);
			delete _file_string;
		}
		list<string>& get_lines() {
			return _file_string->split("\n");
		}
};
