/* 
 * File:   server_tcp.c
 * Author: Arthur Brandao
 *
 * Created on 14 novembre 2018
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "error.h"
#include "server.h"

/* --- Extern --- */
extern int serrno;

/* --- Fonctions privées --- */
boolean server_bind_tcp(Server this, int port) {
    /* Declaration variable */
    int tmp;

    /* Bind */
    this->serv.sin_family = AF_INET;
    this->serv.sin_port = htons(port);
    this->serv.sin_addr.s_addr = htonl(INADDR_ANY);
    tmp = bind(this->socket, (struct sockaddr*) &this->serv, sizeof (struct sockaddr_in));
    if (tmp == ERR) {
        free(this);
        serrno = SEBIND;
        addperror("Impossible de bind la socket");
        return false;
    }

    /* Listen */
    tmp = listen(this->socket, SOMAXCONN);
    if (tmp == ERR) {
        free(this);
        serrno = SEBIND;
        addperror("Impossible d'ecouter");
        return false;
    }
    
    return true;
}

boolean server_accept_tcp(Server this) {
    /* Accept */
    this->socket_client = accept(this->socket, NULL, NULL);
    if (this->socket_client == ERR) {
        serrno = SEACCEPT;
        addperror("Impossible d'accepter");
        return false;
    }
    
    return true;
}

ssize_t server_receive_tcp(Server this, char* buf, size_t size) {
    int tmp;
    /* Lecture message */
    memset(buf, 0, size);
    tmp = read(this->socket_client, buf, size);
    if (tmp == ERR) {
        serrno = SERECEIVE;
        addperror("Impossible de récupèrer les données");
        return ERR;
    }
    return tmp;
}

boolean server_send_tcp(Server this, char* msg) {
    int tmp;
    /* Envoi message */
    tmp = write(this->socket_client, msg, strlen(msg) * sizeof (char));
    if (tmp == ERR) {
        serrno = SESEND;
        addperror("Impossible d'envoyer les données");
        return false;
    }
    
    return true;
}

/* ---  Fonctions publiques --- */
Server server_create_tcp() {
    /* Declaration variable */
    Server this;
    int tmp, option;

    /* Creation socket */
    this = malloc(sizeof (struct server));
    this->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (this->socket == ERR) {
        free(this);
        serrno = SESOCKET;
        addperror("Impossible de créer la socket");
        return NULL;
    }
    this->addr = sizeof (struct sockaddr_in);
    memset(&this->serv, 0, sizeof (struct sockaddr_in));

    /* Indique la possibilité de réutiliser la socket même si elle est en TIME_WAIT */
    option = 1;
    tmp = setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof (option));
    if (tmp == -1) {
        free(this);
        serrno = SESOCKET;
        addperror("Impossible de modifier les options de la socket");
        return NULL;
    }

    /* Lien fonctions */
    this->server_bind = server_bind_tcp;
    this->server_receive = server_receive_tcp;
    this->server_send = server_send_tcp;
    this->server_accept = server_accept_tcp;

    /* Type de serveur */
    this->type = SERV_TCP;

    /* Retour */
    return this;
}

boolean server_close_client(Server this) {
    /* Que pour TCP */
    if (this->type != SERV_TCP) {
        serrno = SETYPE;
        adderror("Type de la socket incorrect");
        return false;
    }
    
    /* Ferme */
    if (close(this->socket_client) == ERR) {
        serrno = SECLOSE;
        addperror("Impossible de fermer la socket");
        return false;
    }
    
    return true;
}
