
#include "Client.hpp"

/*********************************************
 * COPLIENS
 *********************************************/

Client::Client()
	: status(0),
	header_complete(false),
	read_body_size(0),
	assigned_server(NULL),
	assigned_location(NULL),
	_fd(0),
	_port(""),
	_ip(""),
	_lsocket(NULL)
{
	return;
}

Client::Client(int afd, listen_socket *lsocket, std::string aport, std::string aip)
	: status(0),
	header_complete(false),
	read_body_size(0),
	assigned_server(NULL),
	assigned_location(NULL),
	_fd(afd),
	_port(aport),
	_ip(aip),
	_lsocket(lsocket)
{
	return;
}

Client::~Client() {
	return;
}

/* // WIP not sure fo what is more logic here
Client::Client( Client const & src )
	: status			( src.status ),
	header_complete		( src.header_complete ),
	read_body_size		( src.read_body_size ),
	assigned_server		( src.assigned_server ),
	assigned_location	( src.assigned_location ),
	_fd					( src._fd ),
	_port				( src._port ),
	_ip					( src._ip ),
	_lsocket			( src._lsocket )
{
	raw_request = src.raw_request;
	response = src.response;
//	buf = strdup(src.buf); // TODO: this doesn't work
	return;
}
 */

/* // WIP placeholder because of const values
Client &	Client::operator=( Client const & rhs )
{
	if ( this != &rhs )
	{
		// stuff
	}
	return *this;
}
 */

/*********************************************
 * PUBLIC MEMBER FUNCTIONS
 *********************************************/

// http headers :
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
// https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests
// https://www.tutorialspoint.com/http/http_requests.htm
void	Client::parse_request(std::vector<ServerConfig> &servers)
{
	std::map<std::string, std::string>	headers;
	std::string							body;

// DEBUG
// std::cout << "\nREQUEST ____________\n" << raw_request << "\n_____________\n";
	clear_request(); // not mandatory

	_parse_request_line();
	if (status)
		return;
	_parse_request_headers();
	assigned_server = ::_determine_process_server(this, servers);
	assigned_location = ::_determine_location(*assigned_server, _request.abs_path);
	_check_request_errors();
	if (status)
		return;
	_parse_port_hostname(this->get_rq_headers("Host"));

/*	dont clear raw_request, we need it for future reparsing of body
	see call of parse_request() in _read_request()  */
	// raw_request.clear(); 
}

void	Client::parse_request_body()
{
	// TODO: check error and adjust status
	_request.body = ::parse_http_body(raw_request);
	if (_request.body.size() > assigned_server->client_body_limit)
		status = 413;
}

bool	Client::fill_script_path(std::string script)
{
	size_t		pos;
	int			len = script.size();
	std::string	path = this->get_rq_abs_path();
	std::string	tmp;

	pos = path.find(script);
	if (pos == 0)
	{
		tmp = path.substr(0, pos + len);
		_request.script.path = "./srcs" + tmp; // TODO: root path ?
		_request.script.info = path.substr(pos + len);
		return true;
	}
	return false;
}

void	Client::clear()
{
	clear_request();
	header_complete = false;
	read_body_size = 0;
	assigned_server = NULL;
	assigned_location = NULL;	
	raw_request.clear();
	response.clear();
	status = 0;
}

void	Client::clear_request()
{
	clear_script();
	_request.method = UNKNOWN;
	_request.uri.clear();
	_request.version.clear();
	_request.headers.clear();
	_request.body.clear();
	_request.abs_path.clear();
	_request.query.clear();
	_request.port.clear();
	_request.hostname.clear();
}

void	Client::clear_script()
{
	_request.script.path.clear();
	_request.script.info.clear();
}


/*********************************************
 * GETTERS
 *********************************************/

// client side
int					 Client::get_cl_fd()	const { return _fd; }
const std::string	&	Client::get_cl_ip() 		const { return _ip; }
const std::string	&	Client::get_cl_port()		const { return _port; }
const listen_socket *	Client::get_cl_lsocket()	const { return _lsocket; }

// requette
http_method 	Client::get_rq_method()		const { return _request.method; }
std::string  	Client::get_rq_method_str() const
	{ return ::http_methods_to_str(_request.method); }
std::string		Client::get_rq_uri()		const { return _request.uri; }
std::string		Client::get_rq_abs_path()	const { return _request.abs_path; }
std::string		Client::get_rq_query()		const { return _request.query; }
std::string		Client::get_rq_version()	const { return _request.version; }
std::string		Client::get_rq_body()		const { return _request.body; }
std::string		Client::get_rq_port()		const { return _request.port; }
std::string		Client::get_rq_hostname()	const { return _request.hostname; }
std::string		Client::get_rq_script_path()const { return _request.script.path; }
std::string		Client::get_rq_script_info()const { return _request.script.info; }
std::string		Client::get_rq_headers(const std::string & key) const
{
	std::map<std::string, std::string>::const_iterator	it;

	it = _request.headers.find(::str_tolower(key));
	if (it == _request.headers.end())
		return "";
	return it->second;
}


/*********************************************
 * PRIVATE MEMBER FUNCTIONS
 *********************************************/

void	Client::_parse_request_line()
{
	std::vector<std::string>	line;
	int							ret;

	ret = ::parse_http_first_line(raw_request, line);
	if (ret != 3)
	{
		std::cerr << "err _parse_first_line(): wrong number of elements (" << ret << " instead of 3)\n";
		status = 400; // "bad request"
	}
	else
	{
		_request.method = str_to_http_method(line[0]);
		_request.uri = line[1];
		_parse_request_uri(line[1]);
		_request.version = line[2];
	}
}

void	Client::_parse_request_uri( std::string uri )
{
	size_t		pos;

	pos = uri.find("?");
	if (pos != std::string::npos)
		_request.query = uri.substr(pos + 1);
	else
		_request.query = "";
	_request.abs_path = uri.substr(0, pos);
}

void	Client::_parse_request_headers()
{
	// TODO: check error and adjust status
	_request.headers = ::parse_http_headers(raw_request);
}

void	Client::_parse_port_hostname(std::string host)
{
	size_t	pos;

	if (host == "")
		std::cerr << "no host\n";

	pos = host.find(':');
	// port :
	if (pos == std::string::npos)
		_request.port = "4040"; // TODO: make equal to default port in config
	else
		_request.port = host.substr(pos);
	if (_request.port == ":")
		_request.port = "";
	// hostname :
	_request.hostname = host.substr(0, pos);
}

void	Client::_check_request_errors()
{
//////////////////////
// Request line checks
	if (_request.method == UNKNOWN)
		status = 501;
	else if (_request.version.compare(0, sizeof("HTTP/1") - 1, "HTTP/1") != 0)
		status = 505;
	else if (!(assigned_location->allow_methods & _request.method))
	{
		status = 405;
		response.append("Allow: ");
		response.append(::http_methods_to_str(assigned_location->allow_methods));
		response.append(CRLF);
	}
	else if (assigned_location->redirect_status)
	{ // Weird behavior. Sometimes, the web browser seems to wait for a complete response until timeout.
		// (for codes 301, 302, 303, 307, and 308)
		status = assigned_location->redirect_status;
		response.append("Location: ");
		response.append(assigned_location->redirect_uri);
		response.append(CRLF CRLF);
	}

	if (status)
		return;

/////////////////
// Headers checks
	if (!this->get_rq_headers("Content-Length").empty()
	&& ::atoi(this->get_rq_headers("Content-Length").c_str()) > (int)assigned_server->client_body_limit)
		status = 413;
	
	return;
}

/*********************************************
 * OVERLOAD
 *********************************************/

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

