#ifndef REQUEST_H
#define REQUEST_H

#include <stdint.h>
#include <stdbool.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#include "buffer.h"

enum socks_req_cmd {
    socks_req_cmd_connect = 0x01,
    socks_req_cmd_bind = 0x02,
    socks_req_cmd_associate = 0x03,
};

enum socks_addr_type {
    socks_req_addr_ipv4 = 0x01,
    socks_req_addr_domain = 0x03,
    socks_req_addr_ipv6 = 0x04,
};

union socks_addr {
    char fqdn[0xff];
    struct sockaddr_in ipv4;
    struct sockaddr_in6 ipv6;
};

struct request {
    enum socks_req_cmd cmd;
    enum socks_addr_type dest_addr_type;
    union socks_addr dest_addr;
    in_port_t dest_port;
};

enum request_state {
    request_version,
    request_cmd,
    request_rsv,
    request_atyp,
    request_dstaddr_fqdn,
    request_dstaddr,
    request_dstport,
    request_done,
    request_error,
    request_error_unsupported_version,
    request_error_unsupported_atyp,
};

struct request_parser {
    struct request * request;
    enum request_state state;

    uint8_t n;
    uint8_t i;  
};

enum socks_response_status {
    status_succeeded = 0x00,
    status_general_SOCKS_server_failure = 0x01,
    status_connectino_not_allowed_by_ruleset = 0x02,
    status_network_unreachable = 0x03,
    status_host_unreachable = 0x04,
    status_connection_refused = 0x05,
    status_ttl_expired = 0x06,
    status_command_not_supported = 0x07,
    status_address_type_not_supported = 0x08,
};

void request_parser_init(struct request_parser * p);

enum request_state request_consume(buffer * b, struct request_parser * p, bool * errored);

bool request_is_done(const enum request_state state, bool * errored);

void request_close(struct request_parser *p);

extern int request_marshall(buffer * b, const enum socks_response_status status);

enum socks_response_status errno_to_socks(int e);

enum socks_response_status cmd_resolve(struct request * request, struct sockaddr ** originaddr, socklen_t * origin_len, int * domain);

#endif