#define _SVID_SOURCE 1
//#define _DEFAULT_SOURCE 1
#define _POSIX_C_SOURCE 200112L

#include <stdio.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <fcntl.h>
#include <string.h>
#include "shr_mem.h"
#include "error.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

sem_t * fullSem = NULL;
sem_t * emptySem = NULL;

int createShm(int totalSize, void ** shmPointer) {
    int fd;
    if ((fd = shm_open("/BottlerSHMem", O_CREAT | O_RDWR, S_IRWXU)) < 0)
        printSystemError("shm_open() -- ERROR");
    ftruncate(fd, totalSize);
    * shmPointer = mmap(0, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    return fd;
}

/*
char * attachShm(int shmId) {
    char *retAddr = shmat(shmid, addr, flags);
    if (retAddr == (void *) -1)
        printSystemError("shmat() -- ERROR");
    return retAddr;
}
*/

int writeShm(void * shmPointer, char * str, int len) {
    if (fullSem == NULL) {
        if ((fullSem = sem_open("/FULL", O_CREAT | O_RDWR, S_IRWXU, 1023)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (emptySem == NULL) {
        if ((emptySem = sem_open("/EMPTY", O_CREAT | O_RDWR, S_IRWXU, 0)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (sem_wait(fullSem) < 0)
        printSystemError("sem_wait() -- ERROR");
    
    if (memcpy(shmPointer, str, len) == NULL)
        return EXIT_FAILURE;

    if (sem_post(emptySem) < 0)
        printSystemError("sem_post() --  ERROR");

    return EXIT_SUCCESS;
}

int readShmDeprecated(char * shmPointer, char * str, int len) {
    if (fullSem == NULL) {
        if ((fullSem = sem_open("/FULL", O_CREAT | O_RDWR, S_IRWXU, 1023)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (emptySem == NULL) {
        if ((emptySem = sem_open("/EMPTY", O_CREAT | O_RDWR, S_IRWXU, 0)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (sem_wait(emptySem) < 0)
        printSystemError("sem_wait() -- ERROR");

    if (memcpy(str, shmPointer, len) == NULL)
        return EXIT_FAILURE;

    if (sem_post(fullSem) < 0)
        printSystemError("sem_post() --  ERROR");

    return EXIT_SUCCESS;
}

void terminateShm(const char * name, int fd, void * shmPointer, int totalSize) {
    if (shm_unlink(name) == -1)
        printSystemError("shm_unlink() -- ERROR");
    munmap(shmPointer, totalSize);
    if (close(fd) == -1)
        printSystemError("close() -- ERROR");
    return;
}

int copyShm(char * destPointer, char * srcPointer, int len) {
    int i;
    for (i = 0; srcPointer[i] != '\n' && srcPointer[i] != '\0' && i < len; i++) {
        destPointer[i] = srcPointer[i];
    }
    destPointer[i++] = '\0';

    return i;
}

int readShm(char * shmPointer, char * str, int len) {
    if (fullSem == NULL) {
        if ((fullSem = sem_open("/FULL", O_CREAT | O_RDWR, S_IRWXU, 1023)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (emptySem == NULL) {
        if ((emptySem = sem_open("/EMPTY", O_CREAT | O_RDWR, S_IRWXU, 0)) == SEM_FAILED)
            printSystemError("sem_open() -- ERROR");
    }
    if (sem_wait(emptySem) < 0)
        printSystemError("sem_wait() -- ERROR");

    int copied = copyShm(str, shmPointer, len);
    // Creo que es lo mismo si hacemos:
    /*
    memcpy(str, shmPointer, len);
    *shmPointer += len;
    */

    if (sem_post(fullSem) < 0)
        printSystemError("sem_post() --  ERROR");

    return copied;
}