#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <time.h>

typedef struct {
    int fd;
    int epoll_fd;
} Handles;

Handles h, *fb = &h;
// we will initialize fb->fd later in client();

#define SERVER_PORT 3003
#define MESSAGE "something"

void server() {
    int servfd = socket(AF_INET, SOCK_STREAM, 0);
    int yes = 1;
    setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = htonl(INADDR_ANY);
    serv.sin_port = htons(SERVER_PORT);

    if (bind(servfd, (struct sockaddr *) &serv, sizeof(serv)) || listen(servfd, 8))
        return;
    printf("Server: listening\n");

    int sockfd = accept(servfd, NULL, NULL);
    if (sockfd) {
        int n;
        char buff[1024];
        printf("Server: got a connection\n");

        while ((n = read(sockfd, buff, sizeof(buff) - 1)) > 0) {
            buff[n] = 0;
            printf("Server: got msg %s\n", buff);
        }
        close(sockfd);
    }
    close(servfd);
    printf("Server: shut down\n");
}

int connect_to_server() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    serv.sin_port = htons(SERVER_PORT);
    for (;;) {
        if (!connect(sockfd, (struct sockaddr *) &serv, sizeof(serv)))
            break;
        printf("Client: connect error\n");
        sleep(1);
    }
    return sockfd;
}

void epoll_add(Handles *fb) {
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.ptr = fb;
    ev.events = EPOLLIN | EPOLLOUT | EPOLLWRNORM | EPOLLERR | EPOLLHUP;
    if (0 != epoll_ctl(fb->epoll_fd, EPOLL_CTL_ADD, fb->fd, &ev))
        printf("epoll_ctl_add fd %d failed\n", fb->fd);
    else
        printf("epoll_ctl_add fd %d succeeded\n", fb->fd);
}

void *client(void *args) {
    fb->fd = connect_to_server();
    int epoll_fd = fb->epoll_fd = epoll_create(512);

    epoll_add(fb);
    for (;;) {
        int i;
        struct epoll_event events[4];
        memset(&events[0], 0, sizeof(events));
        int nevents = epoll_wait(epoll_fd, events, 4, -1);

        for (i = 0; i < nevents; i++) {
            Handles *fb = events[i].data.ptr;
            if (events[i].events & EPOLLOUT) {
                send(fb->fd, MESSAGE, strlen(MESSAGE), MSG_NOSIGNAL);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fb->fd, NULL);
                printf("epoll_ctl_del fd %d\n", fb->fd);
            }
        }
    }
    return NULL ;
}

void *client2(void *args) {
    for (;;) {
        sleep(2);
        epoll_add(fb);
    }
    return NULL ;
}

int main() {
    pid_t pid = fork();
    if (pid == 0)
        server();
    else if (pid > 0) {
        pthread_t c1, c2;
        pthread_create(&c1, NULL, client, NULL);
        pthread_create(&c2, NULL, client2, NULL);
        pthread_join(c1, NULL);
        pthread_join(c2, NULL);
    }
    return 0;
}
