#include "album.h"

extern int errno;

typedef struct req {
    int cmd, arg1, len;
    struct req *next;
} req;

ssize_t readn(int fd, void *vptr, size_t n) {
    size_t  nleft;
    ssize_t nread;
    char    *ptr;
    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR)
                nread = 0;
            else
                return(-1);
        } else if (nread == 0)
            break;

        nleft -= nread;
        ptr   += nread;
    }
    return(n - nleft);
}

int main(int argc, char **argv) {
    struct sockaddr_in me;
    me.sin_family = AF_INET;
    me.sin_port = htons(strtol((char *)argv[1], 0, 10));
    me.sin_addr.s_addr = inet_addr("127.0.0.1");
    int srvsock = socket(AF_INET, SOCK_STREAM, 0);
    if(srvsock == -1) { printf("socket returned -1\n"); exit(0); }
    int retval = bind(srvsock, (struct sockaddr *) &me, sizeof(me));
    if(retval == -1) { printf("bind returned -1\n"); exit(0); }
    retval = listen(srvsock, 1);
    if(retval == -1) { printf("listen returned -1\n"); exit(0); }
    int qemusock = accept(srvsock, 0, 0);
    if(qemusock == -1 && errno != EAGAIN) { printf("accept returned -1\n"); exit(0); }
    printf("connection established\n");
    
    int mypid = getpid(), qemupid;
    send(qemusock, &mypid, 4, 0);
    readn(qemusock, &qemupid, 4);

    FILE *f[NUM_ALB_TYPES];
    int i = 0;
    for(i = 0; i < NUM_ALB_TYPES; i++) {
        char fname[100];
        sprintf(fname, "data%d", i);
        f[i] = fopen(fname, "w+");
    }

    req *reqhead = 0, *reqtail = 0;
    int shmno = 0;
    while(1) {    
        fd_set rfds;
        do {
            struct timeval tv;
            FD_ZERO(&rfds);
            FD_SET(qemusock, &rfds);
            tv.tv_sec = 0;
            tv.tv_usec = 0;
            int retval = select(qemusock+1, &rfds, NULL, NULL, &tv);
            if(FD_ISSET(qemusock, &rfds)) {
                req *newreq = malloc(sizeof(req));
                newreq->next = 0;
                readn(qemusock, newreq, 12);
                if(reqhead == 0) {
                    reqhead = reqtail = newreq;
                }
                else {
                    reqtail->next = newreq;
                    reqtail = newreq;
                }
            }
        } while(FD_ISSET(qemusock, &rfds));
    
        if(reqhead != 0) {
            printf("%d %d %d\n", reqhead->cmd, reqhead->arg1, reqhead->len);
            char name[100];
            if(reqhead->cmd < NUM_ALB_TYPES) { // store cmd (transfer data from qemu to album)
                sprintf(name, "%d-%d", qemupid, reqhead->arg1);
                int shmfd = shm_open(name, O_RDWR, 0);
                void *shmaddr = mmap(0, reqhead->len, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
                fseek(f[reqhead->cmd], 0, SEEK_END);
                fwrite(shmaddr, 1, reqhead->len, f[reqhead->cmd]);
                munmap(shmaddr, reqhead->len);
                shm_unlink(name);
            }
            else if(reqhead->cmd < 2*NUM_ALB_TYPES) { // load cmd (transfer data from album to qemu)
                sprintf(name, "%d-%d", mypid, shmno);
                int shmfd = shm_open(name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
                ftruncate(shmfd, reqhead->len);
                void *shmaddr = mmap(0, reqhead->len, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0);
                fseek(f[reqhead->cmd-NUM_ALB_TYPES], reqhead->arg1, SEEK_SET);
                fread(shmaddr, 1, reqhead->len, f[reqhead->cmd-NUM_ALB_TYPES]);
                send(qemusock, &shmno, 4, 0);
                shmno++;
                munmap(shmaddr, reqhead->len);
            }
            else { // multi-load cmd
                sprintf(name, "%d-%d", qemupid, reqhead->arg1);
                int shmfd1 = shm_open(name, O_RDWR, 0);
                int *multireq = mmap(0, reqhead->len, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd1, 0);
                int respsz = *multireq;
                int *reqs = multireq+1;
                int entries = (reqhead->len-1)/4;
                char name2[100];
                sprintf(name2, "%d-%d", mypid, shmno);
                int shmfd2 = shm_open(name2, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
                ftruncate(shmfd2, entries*respsz);
                void *out = mmap(0, entries*respsz, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd2, 0);
                int o;
                for(o = 0; o < entries; o++) {
                    fseek(f[reqhead->cmd-2*NUM_ALB_TYPES], reqs[o], SEEK_SET);
                    fread(out+respsz*o, 1, respsz, f[reqhead->cmd-2*NUM_ALB_TYPES]);
                }
                send(qemusock, &shmno, 4, 0);
                shmno++;
                munmap(reqs, reqhead->len);
                shm_unlink(name);
                munmap(out, entries*respsz);
            }
            req *tmp = reqhead;
            reqhead = reqhead->next;
            free(tmp);
        }
    }
    
    return 0;
}
