
#ifndef WEBSERV_HPP
# define WEBSERV_HPP

# include <map>
# include <vector>
# include <exception>		// exception, what
# include <stdexcept>		// runtime_error, invalid_argument
# include <sys/epoll.h>		// epoll
# include <fcntl.h>			// fcntl
# include <sys/wait.h>		// waitpid
# include <csignal>			// signal
# include <fstream>			// ifstream
# include <sstream>			// stringstream
# include <cerrno>			// errno
# include <unistd.h>		// close, access
# include <iostream>		// cout, cin
# include <cstring>			// memset
# include <sys/socket.h>	// socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
# include <arpa/inet.h>		// htonl, htons, ntohl, ntohs, inet_addr, inet_ntoa
# include <netinet/in.h>	// sockaddr_in, struct in_addr
// # include <netinet/ip.h> // usefull for what ? -> 'man (7) ip' says it's a superset of 'netinet/in.h'
# include <algorithm>		// find
# include <string>			// string
# include <cstdio>			// perror, remove
# include <cstdlib>			// strtol, strtoul
# include <dirent.h>		// opendir()
# include <locale>			// isalpha, local

# include "Client.hpp"
# include "ServerConfig.hpp"
# include "utils.hpp"
# include "http_status.hpp"
# include "autoindex.hpp"
# include "colors.h"

extern bool	g_run;
extern int	g_last_signal;
void		signal_handler(int signum);

// these might only be TMP
# define FAILURE -1
# define SUCCESS 1

# define MIME_TYPE_DEFAULT "application/octet-stream"

class Webserv
{
	public:
	// base.cpp
		Webserv();
		// Webserv(Webserv const &src);

	// what should it take as arg, *, &, ?
//		Webserv(std::vector<ServerConfig>& servers);

		~Webserv();
		// Webserv &operator=(Webserv const &rhs);

	// init.cpp
		void	init_virtual_servers(std::vector<ServerConfig>* servers);
	// run_loop.cpp
		void	run();

	private:
		int	_epfd;
		std::vector<listen_socket>	_listen_sockets;
		std::vector<ServerConfig>	_servers;
		std::vector<Client>			_clients;
		std::map<int, std::string>	_http_status;
		std::map<std::string, std::string>	_mime_types;

	// accept.cpp
		void		_accept_connection(listen_socket &lsocket);
		std::map<std::string, std::string>
					_extract_infos(struct sockaddr_in addr);
	// request.cpp
		void		_request(Client *client);
		int			_read_request(Client *client);
	// response.cpp
		void		_response(Client *client);
		int			_send_response(Client *client);
		void		_append_base_headers(Client *client);
		void		_construct_response(Client *client);
		void		_process_method(Client *client, std::string &path);
		std::string	_replace_url_root(Client *client, std::string path);
		void		_insert_status_line(Client *client);
		void		_error_html_response(Client *client);
		void		_append_body(Client *client, const std::string &body, const std::string &file_extension = "");
	// method_get.cpp
		void		_get(Client *client, std::string &path);
		void		_get_file(Client *client, const std::string &path);
		void		_autoindex(Client *client, const std::string &path);
		std::string	_determine_file_extension(const std::string &path) const;
	// method_post.cpp
		void		_post(Client *client, const std::string &path);
		void		_upload_files(Client *client);
	// method_delete.cpp
		void		_delete(Client *client, const std::string &path);
		void		_delete_file(Client *client, const std::string &path);
	// epoll_update.cpp
		int			_epoll_update(int fd, uint32_t events, int op);
		int			_epoll_update(int fd, uint32_t events, int op, void *ptr);
	// signal.cpp
		void		_handle_last_signal();
	// close.cpp
		void		_close_client(int fd);
		void		_close_all_clients();
		void		_close_all_clients_fd();
		void		_close_all_clients_cgi_fd();
		void		_close_cgi_pipe_rfd(int fd);
		void		_close_all_cgi_pipe_rfd();
		void		_close_all_listen_sockets();
		void		_reopen_lsocket(std::vector<listen_socket>::iterator it);
		void		_handle_epoll_error_lsocket(uint32_t events, std::vector<listen_socket>::iterator it);
		void		_handle_epoll_error_client(uint32_t events, int fd);
	// init.cpp
		void		_bind(int socket_fd, in_port_t port, std::string host);
		void		_listen(int socket_fd, unsigned int max_connections);
		void		_init_http_status_map();
		void		_init_mime_types_map();
	// timeout.cpp
		void		_timeout();
	// cgi.cpp
		bool		_is_cgi(Client *client, std::string path);
		size_t		_cgi_pos(Client *client, std::string &path, size_t pos);
		void		_exec_cgi(Client *client);
		void		_set_env_vector(Client *client, std::vector<std::string> &env_vector);
		void		_set_env_cstr(char *env_cstr[], std::vector<std::string> &env_vector);
		std::string	_dup_env(std::string var, std::string val);
		std::string	_dup_env(std::string var, int i);
		void		_exec_script(Client *client, char *env[]);
		void		_check_script_output(Client *client, std::string & output);
		void		_check_script_status(Client *client, std::string & output);
		size_t		_check_script_fields(const std::string & output, size_t status);
		void		_check_fields_duplicates(Client *client, std::string & output);
		void		_add_script_body_length_header(std::string & output);
		void		_remove_body_leading_empty_lines(std::string & output);

		Client	*_find_cgi_fd(int cgi_fd);
		void	_read_cgi_output(Client *client);
		void	_handle_epoll_error_cgi_fd(uint32_t events, Client *client);
		void	_cgi_epollhup(uint32_t events, Client *client);


	///////////////////////
		class ExecFail : public std::exception
		{
			public :
				virtual const char*	what() const throw() {
					return ("Exec CGI fail");
				};
		};

};

#endif

/*
	HTTP Semantics:
		https://www.rfc-editor.org/rfc/rfc9110.html
		https://www.bortzmeyer.org/9110.html
		https://www.bortzmeyer.org/cours-http-cnam.html
	HTTP/1.1:
		https://www.rfc-editor.org/rfc/rfc9112.html
		https://www.bortzmeyer.org/9112.html
	CGI:
		https://www.rfc-editor.org/rfc/rfc3875.html
*/
