
#include "utils.hpp"

void	throw_test()
{
	static int i = 0;
	++i;
	if (i % 8 == 0)
		throw std::bad_alloc();
}

// notice : the use of getline make it such as
// it doesn't identify multiple delim as one :
// " something \n else "   -> 1 - something
//                            2 - else
// is not the same as :
// " something \n\n else " -> 1 - something
//                            2 - 
//                            3 - else
std::vector<std::string> split(std::string input, char delimiter)
{
    std::vector<std::string>	answer;
    std::stringstream			ss(input);
    std::string					temp;
 
    while (getline(ss, temp, delimiter))
        answer.push_back(temp);
 
    return answer;
}

std::vector<std::string>
	split_trim(std::string input, std::string delim, char ctrim)
{
	std::vector<std::string>	split_str;
	std::string					tmp;
	size_t						start = 0;
	size_t						end = 0;
	size_t						len = 0;

	while (end != NPOS)
	{
		end = input.find(delim, start);
		len = end - start;
		if (end == NPOS)
			len = end;
		tmp = input.substr(start, len);
		if (ctrim != '\0')
			tmp = trim(tmp, ctrim);
		if (tmp.size() != 0)
			split_str.push_back( tmp );
		start = end + delim.size();
	}
	return split_str;
}

std::string	trim(std::string str, char del)
{
	size_t	pos;

	// delete leadings del
	pos = str.find_first_not_of(del);
	if (pos == NPOS)
		pos = str.size();
	str = str.substr(pos);

	// delete trailing del
	pos = str.find_last_not_of(del);
	if (pos != NPOS)
		str = str.substr(0, pos + 1);

	return str;
}

//// trim a set of char
//std::string	trim(std::string str, std::string del)
//{
//	std::string	new_str;
//
//	while (new_str.compare(str) != 0)
//	{
//		for (size_t i = 0; i < del.size(); i++)
//			trim(str, del[i]);
//	}
//	return str;
//}

std::string	itos(int n)
{
	std::stringstream	strs;

	strs << n;
	return ( strs.str() );
}

bool	isNumeric(std::string str)
{
	for (size_t i = 0; i < str.length(); i++)
	{
		if (std::isdigit(str[i]) == false)
			return false;
	}
	return true;
}

bool	isNumeric_btw(int low, int high, std::string str)
{
	for (size_t i = 0; i < str.length(); i++)
	{
		if (std::isdigit(str[i]) == false)
			return false;
	}
	int n = std::strtol(str.c_str(), NULL, 10);
	if (n < low || n > high)
		return false;
	return true;
}

http_method	str_to_http_method(std::string &str)
{
	if (str == "GET")
		return GET;
	else if (str == "POST")
		return POST;
	else if (str == "DELETE")
		return DELETE;
	return UNKNOWN;
}

std::string	http_methods_to_str(unsigned int methods)
{
	std::string	str;

	if (methods & GET)
		str.append("GET");
	if (methods & POST)
	{
		if (!str.empty())
			str.append(", ");
		str.append("POST");
	}
	if (methods & DELETE)
	{
		if (!str.empty())
			str.append(", ");
		str.append("DELETE");
	}

	return (str);
}

# include <iostream>

file_type	eval_file_type(const std::string &path)
{
	struct stat s;

	if (stat(path.c_str(), &s) != -1)
	{
		if (S_ISREG(s.st_mode))
			return (IS_FILE);
		else if (S_ISDIR(s.st_mode))
			return (IS_DIR);
	}
	else
	{
		std::perror("err stat()");
	}

	return (IS_OTHER);
}

size_t	eval_file_access(const std::string &path, int mode)
{
	if (::access(path.c_str(), F_OK) == -1)
	{
		std::perror("err access()");
		return 404; // NOT_FOUND, file doesn't exist
	}

	if (::access(path.c_str(), mode) == -1)
	{
		std::perror("err access()");
		return 403; // FORBIDDEN, file doesn't have access permission
	}
	return 0;
}

void
	replace_all_substr(
		std::string &str,
		const std::string &ori_substr,
		const std::string &new_substr)
{
	if (ori_substr.empty())
		return;
	size_t	pos = 0;
	while (1)
	{
		pos = str.find(ori_substr, pos);
		if (pos == NPOS)
			break;
		str.replace(pos, ori_substr.size(), new_substr);
		pos += new_substr.size();
	}
}

std::string	str_tolower(std::string str)
{
	std::transform(str.begin(), str.end(), str.begin(), ::tolower);
	return str;
}

// identify a line in a string, by delim (ex. '\n')
// delete this line from the string (and the following nl sequence characters)
// and return the deleted line (without the followinf nl sequence characters)
std::string
	extract_line(std::string & str, size_t pos, std::string delim)
{
	std::string	del_str;
	size_t		begin;
	size_t		end;
	size_t		len;

	begin = str.rfind(delim, pos);
	if (begin == NPOS)
		begin = 0;
	else if (begin < pos)
		begin += delim.size();

	end = str.find(delim, pos);
	len = end;
	if (end != NPOS)
		len = end - begin;

	del_str = str.substr(begin, len);
	str.erase(begin, len +  delim.size());
	return del_str;
}

// get a line in a string, by delim
// same as extract, except it doesn't delete it
std::string    get_line(std::string str, size_t pos, std::string delim)
{
	std::string	ret;

	ret = ::extract_line(str, pos, delim);
	return ret;
}

size_t
	parse_http_headers (
		std::string headers,
		std::map<std::string, std::string> & fields )
{
	std::vector<std::string>			list;
	std::vector<std::string>::iterator	it;
	std::vector<std::string>::iterator	it_end;
	size_t								err_count = 0;
	size_t								pos;
	std::string							key;
	std::string							val;

	list = ::split_trim(headers, CRLF, ' ');

	it_end = list.end();
	for (it = list.begin(); it != it_end; it++)
	{
		pos = (*it).find(':');
		if (pos == NPOS)
		{
			err_count++;
			continue;
		}
		key = (*it).substr(0, pos);
		if ( key.find(' ') != NPOS )
		{
			err_count++;
			continue;
		}
//		bad idea, in cgi we need to have the original value
//		key = ::str_tolower(key); // to make "key" case_insensitive
		val = (*it).substr(pos + 1);
		val = ::trim(val, ' ');
		fields.insert( std::pair<std::string, std::string>(key, val) );
	}
	return err_count;
}

void	str_map_key_tolower(std::map<std::string, std::string> & mp)
{
	std::map<std::string, std::string>				new_mp;
	std::map<std::string, std::string>::iterator	it;
	std::string										key;
	std::string										value;

	for (it = mp.begin(); it != mp.end(); it++)
	{
		key = it->first;
		value = it->second;
		key = ::str_tolower(key);
		new_mp.insert( std::pair<std::string, std::string>(key, value) );
	}
	mp.swap(new_mp);
}

// DEBUG
void	print_special(std::string str)
{
	char	c;

	for (size_t i = 0; i < str.size(); i++)
	{
		c = str[i];
		if (c == '\r')
			std::cout << YELLOW << "\\r" << RESET;
		else if (c == '\n')
			std::cout << YELLOW << "\\n" << RESET << "\n";
		else
			std::cout << c;
		fflush(stdout);
	}
}

bool	operator==(const listen_socket& lhs, int fd)
	{ return lhs.fd == fd; }

bool	operator==(int fd, const listen_socket& rhs)
	{ return fd == rhs.fd; }

