#include "server.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <sys/poll.h>

// #define READ_SERVER
// #define WRITE_SERVER

#define client_printf(buf, fd, ...) \
    sprintf((buf), __VA_ARGS__); \
    write((fd), (buf), strlen((buf))); \

#define read_from_file_head(fd, buf) \
    struct flock lock = { .l_type = F_RDLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = SEAT_NUM * 2, }; \
    if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl setlk"); \
    lseek((fd), 0, SEEK_SET);  \
    read((fd), (buf), SEAT_NUM*2);  \
    if (read(fd, buf, SEAT_NUM * 2) == -1) perror("can't read file"); \
    lock.l_type = F_UNLCK; \
    if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl unlock"); \

#define EXIT_CMD "exit"
#define PAY_CMD "pay"
#define SEAT_CMD "seat"

const unsigned char IAC_IP[3] = "\xff\xf4";
const char* file_prefix = "./csie_trains/train_";
const char* accept_read_header = "ACCEPT_FROM_READ";
const char* accept_write_header = "ACCEPT_FROM_WRITE";
const char* welcome_banner = "======================================\n"
                             " Welcome to CSIE Train Booking System \n"
                             "======================================\n";

const char* lock_msg = ">>> Locked.\n";
const char* exit_msg = ">>> Client exit.\n";
const char* cancel_msg = ">>> You cancel the seat.\n";
const char* full_msg = ">>> The shift is fully booked.\n";
const char* seat_booked_msg = ">>> The seat is booked.\n";
const char* no_seat_msg = ">>> No seat to pay.\n";
const char* book_succ_msg = ">>> Your train booking is successful.\n";
const char* invalid_op_msg = ">>> Invalid operation.\n";

#ifdef READ_SERVER
char* read_shift_msg = "Please select the shift you want to check [902001-902005]: ";
#elif defined WRITE_SERVER
char* write_shift_msg = "Please select the shift you want to book [902001-902005]: ";
char* write_seat_msg = "Select the seat [1-40] or type \"pay\" to confirm: ";
char* write_seat_or_exit_msg = "Type \"seat\" to continue or \"exit\" to quit [seat/exit]: ";
#endif

static void init_server(unsigned short port);
// initailize a server, exit for error

static void init_request(request* reqP);
// initailize a request instance

static void free_request(request* reqP);
// free resources used by a request instance

int accept_conn(void);
// accept connection

static void getfilepath(char* filepath, int extension);
// get record filepath

int handle_read(request* reqP) {
    /*  Return value:
     *      1: read successfully
     *      0: read EOF (client down)
     *     -1: read failed
     */
    int r;
    char buf[MAX_MSG_LEN];
    size_t len;

    memset(buf, 0, sizeof(buf));

    // Read in request from client
    r = read(reqP->conn_fd, buf, sizeof(buf));
    if (r < 0) return -1;
    if (r == 0) return 0;
    char* p1 = strstr(buf, "\015\012"); // \r\n
    if (p1 == NULL) {
        p1 = strstr(buf, "\012");   // \n
        if (p1 == NULL) {
            if (!strncmp(buf, IAC_IP, 2)) {
                // Client presses ctrl+C, regard as disconnection
                fprintf(stderr, "Client presses ctrl+C....\n");
                return 0;
            }
            // incomplete message, (EOF)
            return 0;
        }
    }

    len = p1 - buf + 1;
    memmove(reqP->buf, buf, len);
    reqP->buf[len - 1] = '\0';
    reqP->buf_len = len-1;
    return 1;
}

#ifdef READ_SERVER
int print_train_info(request *reqP) {
    // check lock of every seat
    // 1. has a readlock: CHOSEN
    // 2. has a writelock: PAID (ing)
    // 3. no lock: just read it and output
    char buf[MAX_MSG_LEN] = {0};
    int fd = trains[reqP->booking_info.shift_id - TRAIN_ID_START].file_fd, buf_len = 0;
    struct flock lock = {
        .l_whence = SEEK_SET,
        .l_len = 1,
    };
    for (int i = 0; i < SEAT_NUM; i++) {
        int pos = i * 2;

        lock.l_type = F_WRLCK;
        lock.l_start = pos;
        if (fcntl(fd, F_GETLK, &lock) == -1) { perror("fcntl getlk"); }

        if (lock.l_type == F_RDLCK) {
            buf_len += sprintf(buf + buf_len, "2%c", ((i + 1) % 4 == 0) ? '\n' : ' ');
        } else if (lock.l_type == F_WRLCK) {
            buf_len += sprintf(buf + buf_len, "1%c", ((i + 1) % 4 == 0) ? '\n' : ' ');
        } else {
            // is F_UNLCK, simply read the file
            lock.l_type = F_RDLCK;
            if (fcntl(fd, F_SETLK, &lock) == -1) { perror("fcntl readlock"); }

            lseek(fd, pos, SEEK_SET);
            if (read(fd, buf + buf_len, 2) == -1) {
                perror("can't read file");
                return -1;
            }
            buf_len += 2;

            lock.l_type = F_UNLCK;
            if (fcntl(fd, F_SETLK, &lock) == -1) { perror("fcntl unlock"); }
        }

    }
    client_printf(buf, reqP->conn_fd, "%s", buf);
    return 0;
}
#else
int print_train_info(request *reqP) {
    /*
     * Booking info
     * |- Shift ID: 902001
     * |- Chose seat(s): 1,2
     * |- Paid: 3,4
     */
    char buf[MAX_MSG_LEN*3];
    char chosen_seat[MAX_MSG_LEN] = {0};
    int length_printed = 0;
    for (int i = 0; i < SEAT_NUM; i++) {
        if (reqP->booking_info.seat_stat[i] == CHOSEN) {
            length_printed += sprintf(chosen_seat + length_printed, (length_printed > 0) ? ",%d" : "%d", i + 1);
        }
    }
    char paid[MAX_MSG_LEN] = {0};
    length_printed = 0;
    for (int i = 0; i < SEAT_NUM; i++) {
        if (reqP->booking_info.seat_stat[i] == PAID) {
            length_printed += sprintf(paid + length_printed, (length_printed > 0) ? ",%d" : "%d", i + 1);
        }
    }

    memset(buf, 0, sizeof(buf));
    client_printf(buf, reqP->conn_fd, "\nBooking info\n"
                 "|- Shift ID: %d\n"
                 "|- Chose seat(s): %s\n"
                 "|- Paid: %s\n\n"
                 , reqP->booking_info.shift_id, chosen_seat, paid);
    return 0;
}

int count_available_seat(int fd) {
    char buf[MAX_MSG_LEN];
    read_from_file_head(fd, buf);
    int len = strlen(buf), ret = 0;
    for (int i = 0; i < len; i++) {
        char curr = buf[i];
        if (curr == '0') ret++;
    }
    return ret;
}

// train shits
int seat_chosen_by_client(request *req, int seat_num) {
    return req->booking_info.seat_stat[seat_num - 1] == CHOSEN;
}

enum SEAT get_seat_state(int fd, int seat_num) {
    // 1. check if file is lock by other process (server)
    // 2. check if the seat is chosen by this process
    // if no lock, read from file
    int pos = (seat_num - 1) * 2;
    char buf[1];
    struct flock lock = {
        .l_type = F_WRLCK,
        .l_whence = SEEK_SET,
        .l_start = pos,
        .l_len = 1
    };
    if (fcntl(fd, F_GETLK, &lock) == -1) perror("fcntl getlock");
    // check other process
    if (lock.l_type == F_RDLCK) return CHOSEN;
    else if (lock.l_type == F_WRLCK) return PAID;
    // check current process
    for (int i = 0; i < maxfd; i++) {
        if (requestP[i].conn_fd == -1 || requestP[i].conn_fd == fd) continue;
        if (requestP[i].booking_info.seat_stat[seat_num - 1] == CHOSEN) return CHOSEN;
        // if (requestP[i].booking_info.seat_stat[seat_num - 1] == PAID) return PAID;
    }

    lock.l_type = F_RDLCK;
    if (fcntl(fd, F_SETLK, &lock) == -1) {
        perror("fcntl readlock");
    }
    lseek(fd, pos, SEEK_SET);
    if (read(fd, buf, strlen("0")) == -1) {
        perror("can't read file");
        return -1;
    }
    lock.l_type = F_UNLCK;
    if (fcntl(fd, F_SETLK, &lock) == -1) {
        perror("fcntl unlock");
    }
    switch (buf[0]) {
        case '0':
            return UNKNOWN;
        case '1':
            return PAID;
        default:
            return -1;
    }
}

void set_seat_state(record booking_info, int seat_num, enum SEAT state) {
    // UNKNOWN: release lock (assume already lock)
    // CHOSEN: lock seat state
    // PAID: write seat state
    int fd = booking_info.train_fd, pos = (seat_num - 1) * 2;
    struct flock lock = {
        .l_whence = SEEK_SET,
        .l_start = pos,
        .l_len = 1
    };
    if (state == CHOSEN) {
        lock.l_type = F_RDLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl readlock");
    } else if (state == PAID) {
        // unlock first
        lock.l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl unlock");
        // apply write lock
        lock.l_type = F_WRLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl writelock");
        lseek(fd, pos, SEEK_SET);
        write(fd, "1", strlen("1"));
        // unlock the file
        lock.l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl unlock");
    } else if (state == UNKNOWN) {
        // get lock type
        lock.l_type = F_RDLCK;
        if (fcntl(fd, F_GETLK, &lock) == -1) perror("fcntl getlock");
        if (lock.l_type != F_UNLCK) perror("not readlock, cannot release lock");
        lock.l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) == -1) perror("fcntl unlock");
    } else perror("unknown enum type");
}

void client_choose_seat(request *req, int seat_num) {
    set_seat_state(req->booking_info, seat_num, CHOSEN);
    req->booking_info.num_of_chosen_seats++;
    req->booking_info.seat_stat[seat_num - 1] = CHOSEN;
}

void client_cancel_seat(request *req, int seat_num) {
    set_seat_state(req->booking_info, seat_num, UNKNOWN);
    req->booking_info.num_of_chosen_seats--;
    req->booking_info.seat_stat[seat_num - 1] = UNKNOWN;
}

void client_pay_seats(request *req) {
    enum SEAT *seat_stat = req->booking_info.seat_stat;
    int shift_id = req->booking_info.shift_id, fd = req->booking_info.train_fd;
    for (int i = 0; i < SEAT_NUM; i++) {
        if (seat_stat[i] == CHOSEN) {
            set_seat_state(req->booking_info, i + 1, PAID);
            req->booking_info.seat_stat[i] = PAID;
        }
    }
    req->booking_info.num_of_chosen_seats = 0;
}
#endif

// utils
void init_seat_stat(record *booking_info) {
    int train_fd = booking_info->train_fd;
    memset(booking_info->seat_stat, UNKNOWN, SEAT_NUM);
}

void close_free_client(struct pollfd *fds, int idx, request *req) {
    int fd = fds[idx].fd;
    fds[idx].fd = -1;
#ifdef WRITE_SERVER
    // TODO: drop lock of this client
    if (req->booking_info.num_of_chosen_seats > 0) {
        for (int i = 0; i < SEAT_NUM; i++) {
            if (req->booking_info.seat_stat[i] == CHOSEN)
                set_seat_state(req->booking_info, i + 1, UNKNOWN);
        }
    }
#endif /* ifdef WRITE_SERVER */
    close(fd);
    free_request(&requestP[fd]);
    for (int i = idx; i < alive_conn - 1; i++) {
        fds[i] = fds[i + 1];
    }
    fds[alive_conn - 1].fd = -1;
    alive_conn--;
    printf("client closed\n");
}

int all_digit(const char *str) {
    char *ptr = (char *) str; 
    printf("str = %s\n", str);
    while (*ptr != '\0') { 
        if (! isdigit(*ptr++)) { 
            return 0; // not all digits. 
        } 
    } 
    // all digits 
    return 1; 
}

int main(int argc, char** argv) {

    // Parse args.
    if (argc != 2) {
        fprintf(stderr, "usage: %s [port]\n", argv[0]);
        exit(1);
    }

    int conn_fd;  // fd for file that we open for reading
    char buf[MAX_MSG_LEN*2], filename[FILE_LEN];

    int i,j;

    // reading trains files
    for (i = TRAIN_ID_START, j = 0; i <= TRAIN_ID_END; i++, j++) {
        getfilepath(filename, i);
#ifdef READ_SERVER
        trains[j].file_fd = open(filename, O_RDONLY);
#elif defined WRITE_SERVER
        trains[j].file_fd = open(filename, O_RDWR);
#else
        trains[j].file_fd = -1;
#endif
        if (trains[j].file_fd < 0) {
            ERR_EXIT("open");
        }
    }

    // Initialize server
    init_server((unsigned short) atoi(argv[1]));

    // Loop for handling connections
    fprintf(stderr, "\nstarting on %.80s, port %d, fd %d, maxconn %d...\n", svr.hostname, svr.port, svr.listen_fd, maxfd);

    // multiplexing shits?
    struct pollfd fds[maxfd];
    for (int i = 0; i < maxfd; i++) fds[i].fd = -1;
    fds[0].fd = svr.listen_fd;
    fds[0].events = POLLIN | POLLOUT;
    alive_conn++;
    while (1) {
        // TODO: Add IO multiplexing
        //
        // probably should remove inner while loop and use select() or poll() instead
        // the server handle each client based their state
        // and should not stuck in one client until he dies or something
        // helpful shit: https://notes.shichao.io/unp/ch6/

        // Check new connection
        printf("waiting on poll...\n");
        int ret = poll(fds, alive_conn, 50000);
        printf("ret = %d\n", ret);
        if (ret < 0) {
            perror("poll");
            break;
        } else if (ret == 0) {
            printf("timedout\n");
            break;
        } 

        // check any connection available
        // TODO: probably should shrink fds array everytime client disconnected
        for (int i = 0; i < alive_conn; i++) {
            if (fds[i].revents == 0) continue;

            if (fds[i].fd == svr.listen_fd) {
                conn_fd = accept_conn();
                if (conn_fd < 0) {
                    perror("accept_conn");
                    break;
                }
                int client_fd = requestP[conn_fd].conn_fd;
                fds[alive_conn].fd = client_fd;
                fds[alive_conn++].events = POLLIN;
                // print banner and input message
                client_printf(buf, client_fd, "%s", welcome_banner);
#ifdef READ_SERVER      
                client_printf(buf, client_fd, "%s", read_shift_msg);
#elif defined WRITE_SERVER
                client_printf(buf, client_fd, "%s", write_shift_msg);
#endif
                requestP[conn_fd].status = SHIFT;
            } else if (fds[i].revents & POLLIN) {
#ifdef READ_SERVER      
                int client_fd = fds[i].fd;

                printf("handling read...\n");
                int ret = handle_read(&requestP[client_fd]);
                if (ret < 0) {
                    fprintf(stderr, "bad request from %s\n", requestP[client_fd].host);
                    client_printf(buf, client_fd, "%s", invalid_op_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    continue;
                } else if (ret == 0) {
                    client_printf(buf, client_fd, "%s", exit_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    continue;
                }

                // TODO: this part probably fucked: trains_num
                if (requestP[client_fd].status == SHIFT) {
                    int trains_num;
                    printf("input: %s", requestP[client_fd].buf);
                    if (all_digit(requestP[client_fd].buf) && (trains_num = atoi(requestP[client_fd].buf)) > 0) {
                        printf("trains_num: %d\n", trains_num);
                        if (trains_num < TRAIN_ID_START || trains_num > TRAIN_ID_END) {
                            requestP[client_fd].status = INVALID;
                        }
                    } else if (strcmp(requestP[client_fd].buf, EXIT_CMD) == 0) {
                        client_printf(buf, client_fd, "%s", exit_msg);
                        close_free_client(fds, i, &requestP[client_fd]);
                        continue;
                    } else {
                        requestP[client_fd].status = INVALID;
                    }
                    if (requestP[client_fd].status != INVALID) {
                        requestP[client_fd].booking_info.shift_id = trains_num;
                        requestP[client_fd].booking_info.train_fd = trains[trains_num - TRAIN_ID_START].file_fd;
                        requestP[client_fd].booking_info.num_of_chosen_seats = 0;
                        init_seat_stat(&requestP[client_fd].booking_info);
                    }
                }

                // print read_shift_msg
                if (requestP[client_fd].status == SHIFT) {
                    print_train_info(&requestP[client_fd]);
                    client_printf(buf, client_fd, "%s", read_shift_msg);
                } else if (requestP[client_fd].status == INVALID) {
                    client_printf(buf, client_fd, "%s", invalid_op_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    break;
                }
#elif defined WRITE_SERVER
                int client_fd = fds[i].fd;

                int ret = handle_read(&requestP[client_fd]);
                if (ret < 0) {
                    fprintf(stderr, "bad request from %s\n", requestP[client_fd].host);
                    client_printf(buf, client_fd, "%s", invalid_op_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    break;
                } else if (ret == 0) {
                    client_printf(buf, client_fd, "%s", exit_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    break;
                }

                if (requestP[client_fd].status == SHIFT) {
                    int trains_num;
                    printf("input: %s", requestP[client_fd].buf);
                    // handling input here
                    if (all_digit(requestP[client_fd].buf) && (trains_num = atoi(requestP[client_fd].buf)) > 0) {
                        printf("trains_num: %d\n", trains_num);
                        if (trains_num < TRAIN_ID_START || trains_num > TRAIN_ID_END) {
                            requestP[client_fd].status = INVALID;
                        } else {
                            int avail_seat = count_available_seat(trains[trains_num - TRAIN_ID_START].file_fd);

                            if (avail_seat == 0) {
                                client_printf(buf, client_fd, "%s", full_msg);
                            } else {
                                requestP[client_fd].status = SEAT;
                                requestP[client_fd].booking_info.shift_id = trains_num;
                                requestP[client_fd].booking_info.train_fd = trains[trains_num - TRAIN_ID_START].file_fd;
                                requestP[client_fd].booking_info.num_of_chosen_seats = 0;
                                init_seat_stat(&requestP[client_fd].booking_info);
                            }
                        }
                    } else if (strcmp(requestP[client_fd].buf, EXIT_CMD) == 0) {
                        client_printf(buf, client_fd, "%s", exit_msg);
                        close_free_client(fds, i, &requestP[client_fd]);
                        break;
                    } else {
                        requestP[client_fd].status = INVALID;
                    }
                } else if (requestP[client_fd].status == SEAT) {
                    int seat_num;
                    if (all_digit(requestP[client_fd].buf) && (seat_num = atoi(requestP[client_fd].buf)) > 0) {
                        if (seat_num < 1 || seat_num > SEAT_NUM) {
                            requestP[client_fd].status = INVALID;
                        } else {
                            // check if the seat is chosen by client
                            if (seat_chosen_by_client(&requestP[client_fd], seat_num)) {
                                client_cancel_seat(&requestP[client_fd], seat_num);
                                client_printf(buf, client_fd, "%s", cancel_msg);
                            } else {
                                // or we check seat state
                                int seat_state = get_seat_state(requestP[client_fd].booking_info.train_fd, seat_num);
                                if (seat_state == PAID) {
                                    client_printf(buf, client_fd, "%s", seat_booked_msg);
                                } else if (seat_state == CHOSEN) {
                                    client_printf(buf, client_fd, "%s", lock_msg);
                                } else if (seat_state == -1) {
                                    perror("something got real fucked");
                                    close_free_client(fds, i, &requestP[client_fd]);
                                    continue;
                                } else {// choose the seat
                                    client_choose_seat(&requestP[client_fd], seat_num);
                                }
                            }
                        }
                    } else if (strcmp(requestP[client_fd].buf, EXIT_CMD) == 0) {
                        client_printf(buf, client_fd, "%s", exit_msg);
                        close_free_client(fds, i, &requestP[client_fd]);
                        continue;
                    } else if (strcmp(requestP[client_fd].buf, PAY_CMD) == 0) {
                        if (requestP[client_fd].booking_info.num_of_chosen_seats == 0) {
                            client_printf(buf, client_fd, "%s", no_seat_msg);
                        } else {
                            client_pay_seats(&requestP[client_fd]);
                            client_printf(buf, client_fd, "%s", book_succ_msg);
                            requestP[client_fd].status = BOOKED;
                        }
                    } else {
                        requestP[client_fd].status = INVALID;
                    }
                } else if (requestP[client_fd].status == BOOKED) {
                    if (strcmp(requestP[client_fd].buf, SEAT_CMD) == 0) {
                        requestP[client_fd].status = SEAT;
                    } else if (strcmp(requestP[client_fd].buf, EXIT_CMD) == 0) {
                        client_printf(buf, client_fd, "%s", exit_msg);
                        close_free_client(fds, i, &requestP[client_fd]);
                        continue;
                    } else {
                        requestP[client_fd].status = INVALID;
                    }
                }
                if (requestP[client_fd].status == SHIFT) {
                    client_printf(buf, client_fd, "%s", write_shift_msg);
                } else if (requestP[client_fd].status == SEAT) {
                    print_train_info(&requestP[client_fd]);
                    client_printf(buf, client_fd, "%s", write_seat_msg);
                } else if (requestP[client_fd].status == BOOKED) {
                    print_train_info(&requestP[client_fd]);
                    client_printf(buf, client_fd, "%s", write_seat_or_exit_msg);
                } else if (requestP[client_fd].status == INVALID) {
                    client_printf(buf, client_fd, "%s", invalid_op_msg);
                    close_free_client(fds, i, &requestP[client_fd]);
                    break;
                }
#endif
            }
        }
        // TODO: handle requests from clients
        // TODO: restore train file when client exits

        // close(client_fd);
        // free_request(&requestP[conn_fd]);
    }      

    free(requestP);
    close(svr.listen_fd);
    for (i = 0;i < TRAIN_NUM; i++)
        close(trains[i].file_fd);

    return 0;
}

int accept_conn(void) {

    struct sockaddr_in cliaddr;
    size_t clilen;
    int conn_fd;  // fd for a new connection with client

    clilen = sizeof(cliaddr);
    conn_fd = accept(svr.listen_fd, (struct sockaddr*)&cliaddr, (socklen_t*)&clilen);
    if (conn_fd < 0) {
        if (errno == EINTR || errno == EAGAIN) return -1;  // try again
        if (errno == ENFILE) {
            (void) fprintf(stderr, "out of file descriptor table ... (maxconn %d)\n", maxfd);
                return -1;
        }
        ERR_EXIT("accept");
    }
    
    requestP[conn_fd].conn_fd = conn_fd;
    strcpy(requestP[conn_fd].host, inet_ntoa(cliaddr.sin_addr));
    fprintf(stderr, "getting a new request... fd %d from %s\n", conn_fd, requestP[conn_fd].host);
    requestP[conn_fd].client_id = (svr.port * 1000) + num_conn;    // This should be unique for the same machine.
    num_conn++;
    
    return conn_fd;
}

static void getfilepath(char* filepath, int extension) {
    char fp[FILE_LEN*2];
    
    memset(filepath, 0, FILE_LEN);
    sprintf(fp, "%s%d", file_prefix, extension);
    strcpy(filepath, fp);
}

// ======================================================================================================
// You don't need to know how the following codes are working
#include <fcntl.h>

static void init_request(request* reqP) {
    reqP->conn_fd = -1;
    reqP->client_id = -1;
    reqP->buf_len = 0;
    reqP->status = INVALID;
    reqP->remaining_time.tv_sec = 5;
    reqP->remaining_time.tv_usec = 0;

    reqP->booking_info.num_of_chosen_seats = 0;
    reqP->booking_info.train_fd = -1;
    for (int i = 0; i < SEAT_NUM; i++)
        reqP->booking_info.seat_stat[i] = UNKNOWN;
}

static void free_request(request* reqP) {
    memset(reqP, 0, sizeof(request));
    init_request(reqP);
}

static void init_server(unsigned short port) {
    struct sockaddr_in servaddr;
    int tmp;

    gethostname(svr.hostname, sizeof(svr.hostname));
    svr.port = port;

    svr.listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (svr.listen_fd < 0) ERR_EXIT("socket");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    tmp = 1;
    if (setsockopt(svr.listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&tmp, sizeof(tmp)) < 0) {
        ERR_EXIT("setsockopt");
    }
    if (bind(svr.listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        ERR_EXIT("bind");
    }
    if (listen(svr.listen_fd, 1024) < 0) {
        ERR_EXIT("listen");
    }

    // Get file descripter table size and initialize request table
    maxfd = getdtablesize();
    requestP = (request*) malloc(sizeof(request) * maxfd);
    if (requestP == NULL) {
        ERR_EXIT("out of memory allocating all requests");
    }
    for (int i = 0; i < maxfd; i++) {
        init_request(&requestP[i]);
    }
    requestP[svr.listen_fd].conn_fd = svr.listen_fd;
    strcpy(requestP[svr.listen_fd].host, svr.hostname);

    return;
}


