
#ifndef CLIENT_HPP
# define CLIENT_HPP

# include <iostream>
# include <string>
# include <map>
# include <vector>
# include <string.h>		// strdup
# include <netinet/in.h>	// sockaddr_in, struct in_addr
# include <arpa/inet.h>		// htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
# include "utils.hpp"
# include "ServerConfig.hpp"
# include "colors.h"

struct Script
{
	std::string							path;
	std::string							info;
};

struct MultipartBody
{
	std::map<std::string, std::string>	headers;
	std::string							body;
};

struct Request
{
	http_method							method;
	std::string							target;
	std::string							abs_path;
	std::string							query;
	std::string							version;
	std::map<std::string, std::string>	headers;
	std::string							body;
	std::vector<MultipartBody>			multi_bodys;
	std::string							port;
	std::string							hostname;
	struct Script						script;
};

class Client
{
	public:
		Client();
		Client(int afd, listen_socket *lsocket, std::string aport, std::string aip);
		~Client();
		// Client(Client const &src);
		// Client &operator=(Client const &rhs);

		std::string				raw_request;
		std::string				response;
		unsigned int			status;
		bool					header_complete;
		bool					body_complete;
		bool					request_complete;
		// size_t					read_body_size; // unused for now
		ServerConfig			*assigned_server; // cant be const cause of error_pages.operator[]
		const LocationConfig	*assigned_location;
		int						cgi_pipe_rfd;
		pid_t					cgi_pid;
		std::string				cgi_output;

		// getters
		int						get_cl_fd() const;
		const std::string	&	get_cl_port() const;
		const std::string	&	get_cl_ip() const;
		const listen_socket *	get_cl_lsocket() const;

		// requests getters
		http_method		get_rq_method() const;
		std::string		get_rq_method_str() const;
		std::string		get_rq_target() const;
		std::string		get_rq_abs_path() const;
		std::string		get_rq_query() const;
		std::string		get_rq_version() const;
		std::string		get_rq_body() const;
		std::string		get_rq_port() const;
		std::string		get_rq_hostname() const;
		std::string		get_rq_script_path() const;
		std::string		get_rq_script_info() const;
		std::string		get_rq_headers(const std::string & key) const;

		const std::vector<MultipartBody>	&get_rq_multi_bodys() const;
		const std::string	get_rq_multi_bodys_headers(const std::string & key, std::vector<MultipartBody>::const_iterator body_it) const;

		void	parse_request_headers(std::vector<ServerConfig> &servers);
		void	parse_request_body();
		void	clear();
		void	clear_request();
		void	clear_script();
		void	fill_script_path(std::string &path, size_t pos);
		// DEBUG
		void	print_client(std::string message = "");

	private:
		int				_fd;
		std::string		_port;
		std::string		_ip;
		listen_socket *	_lsocket;
		struct Request	_request;

		void	_parse_request_line();
		void	_parse_request_fields();
		void	_parse_request_target( std::string target );
		void	_parse_port_hostname(std::string host);
		void	_parse_chunked_body(size_t pos);
		void	_parse_multipart_body(size_t pos);
		void	_check_request_errors();

		ServerConfig*
			_determine_process_server(Client *client, std::vector<ServerConfig> &servers);
		const LocationConfig*
			_determine_location(const ServerConfig &server, const std::string &path);
};

bool	operator==(const Client& lhs, const Client& rhs);
bool	operator==(const Client& lhs, int fd);
bool	operator==(int fd, const Client& rhs);

#endif
