
#include "Webserv.hpp"

enum send_return
{
	SEND_IN_PROGRESS, // unused
	SEND_COMPLETE,
	SEND_CLOSE,
};

void	Webserv::_response(Client *client)
{
	int ret = _send_response(client);

	if (g_last_signal)
		_handle_last_signal();

	if (ret == SEND_CLOSE)
	{
		_close_client(client->get_cl_fd());
	}
	else if (ret == SEND_COMPLETE)
	{
		if (client->get_rq_headers("Connection") == "close")
			_close_client(client->get_cl_fd());
		else
		{
			_epoll_update(client->get_cl_fd(), EPOLLIN, EPOLL_CTL_MOD);
			client->clear();
		}
	}
}

int	Webserv::_send_response(Client *client)
{
	ssize_t	ret;

	std::cerr << "send()\n";

	_append_base_headers(client);
	if (!client->status)
		_construct_response(client);
	_insert_status_line(client);
	if (client->status >= 400)
		_error_html_response(client);

	std::cerr << "client->response.size() = " << client->response.size() << "\n"; // DEBUG
	ret = ::send(client->get_cl_fd(), client->response.c_str(), client->response.size(), 0);
	if (ret == -1)
	{
		std::perror("err send()");
		std::cerr << "client.fd =" << client->get_cl_fd() << "\n"; // DEBUG
		return SEND_CLOSE;
	}
	std::cerr << "ret send() = " << ret << "\n"; // DEBUG

	return SEND_COMPLETE;
}

void	Webserv::_append_base_headers(Client *client)
{
	client->response.append("Server: Webserv/0.1" CRLF);

	if (client->get_rq_headers("Connection") == "close")
		client->response.append("Connection: close" CRLF);
	else
		client->response.append("Connection: keep-alive" CRLF);
}

void	Webserv::_construct_response(Client *client)
{
	/* Switch between normal behavior or CGI here ?
	maybe better than in _get(), _post(), ...*/
	_process_method(client);
}

void	Webserv::_process_method(Client *client)
{
	std::cerr << "assigned_location->path = " << client->assigned_location->path << "\n"; // debug
	std::cerr << "allow_methods = " << client->assigned_location->allow_methods << "\n"; // debug

	switch (client->get_rq_method())
	{
		case (GET):
			_get(client); break;
		case (POST):
			_post(client); break;
		case (DELETE):
			_delete(client); break;
		default:
			break;
	}
}

void	Webserv::_insert_status_line(Client *client)
{
	std::string	status_line;

	status_line.append("HTTP/1.1 ");
	status_line.append(_http_status[client->status]);
	status_line.append(CRLF);
	client->response.insert(0, status_line);
}

void	Webserv::_error_html_response(Client *client)
{
	if (!client->assigned_server || client->assigned_server->error_pages[client->status].empty())
	{
		std::string	html_page = HTML_ERROR;
		::replace_all_substr(html_page, STATUS_PLACEHOLDER, _http_status[client->status]);
		_append_body(client, html_page, "html");
	}
	else
		_get_file(client, client->assigned_server->error_pages[client->status]);
}

void	Webserv::_append_body(Client *client, const std::string &body, const std::string &file_extension)
{
	const std::string	&mime_type = _mime_types[file_extension];

	client->response.append("Content-Type: ");
	if (mime_type.empty())
		client->response.append(MIME_TYPE_DEFAULT);
	else
	{
		client->response.append(mime_type);
		if (mime_type.find("text/") != std::string::npos)
			client->response.append("; charset=UTF-8");
	}
	client->response.append(CRLF);

	client->response.append("Content-Length: ");
	std::string	tmp = ::itos(body.size());
	client->response.append(tmp);
	client->response.append(CRLF);

	client->response.append(CRLF);
	client->response.append(body);
}

// Temporary Global Scope. Probably move to Client in the future.
ServerConfig	*_determine_process_server(Client *client, std::vector<ServerConfig> &servers)
{
/*
	http://nginx.org/en/docs/http/request_processing.html
	_determine_process_server() should be complete.
	TODO : test it
*/

	std::string	const &server_name = client->get_rq_headers("Host");
	std::vector<ServerConfig>::iterator	it = servers.begin();
	std::vector<ServerConfig>::iterator	default_server = servers.end();

	while (it != servers.end())
	{
		if (it->host == client->get_cl_lsocket()->host
		&&	it->port == client->get_cl_lsocket()->port)
		{
			if ( std::find(it->server_name.begin(), it->server_name.end(), server_name) != it->server_name.end() )
				break;
			else if (default_server == servers.end())
				default_server = it;
		}
		++it;
	}
	if (it != servers.end())
		return (&(*it));
	else
		return (&(*default_server));
}


const LocationConfig	*_determine_location_COOP_FIX(const ServerConfig &server, const std::string &path)
{
/* Pseudo-code :
- comparer les size(), si location.path > client.path, stop comparaison.
- client.path.compare(0, location.path.size(), location.path)
if ( == 0)
{
	if (location.path.size() == client.path.size())
	{
		FOUND;
	}
	else if (client.path[location.path.size()] == '/')
	{
		FOUND;
	}
}
else
{
	NOT FOUND;
	++next;
}
*/
	std::vector<LocationConfig>::const_iterator	it = server.locations.begin();
	while (it != server.locations.end())
	{
		if (it->path.size() > path.size())
		{
			// prendre en compte l'éventuel "/" final si location est un dossier
			if (it->path.size()-1 > path.size() || it->path[it->path.size()-1] != '/')
			{
				++it;
				continue;
			}
		}
		if (path.compare(0, it->path.size(), it->path) == 0)
		{
			if (it->path.size() == path.size())
				break;
			else if (path[it->path.size()-1] == '/')
				break;
		}
		++it;
	}
	if (it != server.locations.end())
		return (&(*it));
	else
		return (&(server.locations.back()));
}

// Temporary Global Scope. Probably move to Client in the future.
const LocationConfig	*_determine_location(const ServerConfig &server, const std::string &path)
{
	std::cout << "determin location path sent: " << path << '\n';


/// NO FUCKING IDEA WHY BUT...
// basically if 2 strings are identical to a point, compare from 
// longer one or it'll freak out cuz of \0 or something idk
//// Basically: str.compare() from the larger string...

/* RULES ***

If a path coresponds exactly to a location, use that one
if no path coresponds then use the most correct one
	most correct means the most precise branch that is still above
	the point we are aiming for

*/

// IS THERE A WAY TO SIMPLIFY THIS LOGIC ???

// what if i tack on a / at the end of path if there isn't one
// and then compare it to things that are their normal length?

	std::string	tmp = path;
	if (tmp[tmp.size() - 1] != '/')
		tmp.push_back('/');

/test/index.html/
/test/index.html


	for (std::vector<LocationConfig>::const_iterator it = server.locations.begin(); it != server.locations.end(); it++)
	{
		std::cout << it->path << "  --  ";

		if (it->path.size() > tmp.size())
			continue ;

		if (tmp.compare(0, it->path.size(), it->path) == 0)
		{
			std::cout << "checking with last /\n";
			if (it->path.size() == tmp.size())
			{
				std::cout << "path sizes are equal \n";
				return (&(*it));
			}
			else if (tmp[it->path.size() - 1] == '/')
			{
				std::cout << "ends in /\n";
				return (&(*it));
			}
		}

/*
//		std::cout << it->path[it->path.size() - 1] << " ";
// it->path.size() -1 only when path ends in / because
// if path doesn't end in / then we are looking for a file
// meaning all it->paths that end in / are wrong if they >=
	//	if (it->path[it->path.size() - 1] == '/' ? it->path.size() - 1 > path.size() : it->path.size() > path.size())

		if (path[path.size() - 1] == '/' ? it->path.size() > path.size() : it->path.size() - 1 > path.size())
		{
			std::cout << "skipping this one\n";
			continue ;
		}


//		if (it->path[it->path.size() - 1] == '/')
		if (path[path.size() - 1] == '/')
		{
			if (path.compare(0, it->path.size(), it->path) == 0)
			{
				std::cout << "checking with last /\n";
				if (it->path.size() == path.size())
				{
					std::cout << "path sizes are equal \n";
					return (&(*it));
				}
				else if (path[it->path.size() - 1] == '/')
				{
					std::cout << "ends in /\n";
					return (&(*it));
				}
			}
		}
		else
		{
			if (path.size() <= it->path.size())
			{
				std::cout << "path is missing a /\n";
				if (it->path.compare(0, path.size(), path) == 0)
					return (&(*it));
				// means we are looking for /test/test_deeper/
				// with /test/test_deeper
			}
			else
			{
//				if (it->path.compare(0, it->path.size() - 1, path) == 0)
				if (path.compare(0, it->path.size(), it->path) == 0)
				{
					std::cout << "checking without last /\n";
					if (it->path.size() - 1 == path.size())
						return (&(*it));
					else if (path[it->path.size() - 1] == '/')
						return (&(*it));
				}
			}
		}
*/

	}
//	if (it != server.locations.end())
//		return (&(*it));
//	else
		return (&(server.locations.back()));
//	/test/mdr
//	/test/mdr/
//	/test/mdrBST

/* More stuff to check this still works with ***

/test/test_
/test/test_/
/test/test_deeper
/test/test_deeper/
/test/test_deepei
/test/test_deepei/
/test/test_deeperi


*/






}

std::string	Webserv::_determine_file_extension(const std::string &path) const
{
	size_t	dot_pos = path.rfind(".");
	if (dot_pos != std::string::npos && dot_pos + 1 < path.size())
		return ( path.substr(dot_pos + 1) );
	return (std::string(""));
}
