/*
 * stats.c
 *
 */

#define _GNU_SOURCE
#define _XOPEN_SOURCE 600

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>

typedef struct NFSops {
    char *opname;
    uint64_t request_count;
    uint64_t response_count;
} NFSops;

/* NFS operations, the order does matter */
NFSops ops[] = {
    { "null",      0, 0 },
    { "getattr",   0, 0 },
    { "setattr",   0, 0 },
    { "lookup",    0, 0 },
    { "access",    0, 0 },
    { "ireadlink", 0, 0 },
    { "read",      0, 0 },
    { "write",     0, 0 },
    { "create",    0, 0 },
    { "mkdir",     0, 0 },
    { "symlink",   0, 0 },
    { "mkdir",     0, 0 },
    { "remove",    0, 0 },
    { "rmdir",     0, 0 },
    { "rename",    0, 0 },
    { "link",      0, 0 },
    { "readdir",   0, 0 },
    { "readdir+",  0, 0 },
    { "fsstat",    0, 0 },
    { "fsinfo",    0, 0 },
    { "pathconf",  0, 0 },
    { "commit",    0, 0 },
    { NULL, 0, 0 },
};

/* struct para linked list de stats por date */
typedef struct datestats {
    time_t date;
    uint64_t write_ops;
    uint64_t write_bytes;
    uint64_t read_ops;
    uint64_t read_bytes;
    struct datestats *next;
} datestats;

enum {
    REQUEST  = 1,
    RESPONSE = 2
};

#define MAX_LINE_TOKENS 24
#define TOKEN_SEPARATOR ' '

#define STR_REQUEST   "req"
#define STR_RESPONSE  "res"

enum {
    POS_TIMESTAMP = 0,
    POS_TYPE      = 6,
    POS_OPERATION = 9,
    POS_BYTES     = 12
};

enum {
    OP_READ  = 6,
    OP_WRITE = 7
};

int store_str_array(char ***dest, char *src, char sep)
{
    unsigned int i, j, count;
    char **p;

/*    if (!src)
        return -1; */
    p = *dest;

    for (i = j = count = 0; src[i]; i++) {
        if (src[i] == sep) {
            src[i] = 0;
            p[count++] = src + j;
            j = i + 1;
            if (count == MAX_LINE_TOKENS - 2)
                break;

        }
    }
    p[count++] = src + j;
    p[count] = NULL;

    return count;
}

int main(int argc, char *argv[])
{
    int i, type;
    uint64_t read_bytes, write_bytes;
    struct datestats *head, *current, *old;

    FILE *fp;
    char *line;
    char **line_array;
    size_t len = 0;
    ssize_t read;

    fp = fdopen(0, "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    /* manter as estatisticas por data numa linked list */
    head = (struct datestats *)malloc(sizeof(struct datestats));

    current = head;
    current->date = 0;
    current->write_ops = current->write_bytes = 0;
    current->read_ops = current->read_bytes = 0;
    current->next = NULL;

    line_array = malloc(MAX_LINE_TOKENS * sizeof(char *));
    line = malloc(1024 * sizeof(char));

    read_bytes = write_bytes = 0;

    while ((read = getline(&line, &len, fp)) != -1) {

        /* split da linha */
        if (store_str_array(&line_array,line, TOKEN_SEPARATOR) < 12)
            continue;

        /* tipo de pedido */
        if (!strncmp(line_array[POS_TYPE], STR_REQUEST, sizeof(STR_REQUEST) - 1))
            type = REQUEST;
        else if (!strncmp(line_array[POS_TYPE], STR_RESPONSE, sizeof(STR_RESPONSE) - 1))
            type = RESPONSE;
        else
            continue;

        /* increase operation counters */
        i = atoi(line_array[POS_OPERATION]);

        if (type == REQUEST)
            ops[i].request_count++;
        else
            ops[i].response_count++;

        if ( (type == REQUEST && *(line_array[POS_OPERATION]) != '7') ||
            (type == RESPONSE && *(line_array[POS_OPERATION]) != '6'))
            continue;

        /* truncar timestamp aos 10 primeiros digitos */
        line_array[POS_TIMESTAMP][10] = 0;

        time_t t, date;
        uint64_t bytes;

        t = atol(line_array[POS_TIMESTAMP]);
        date = t - t % 3600;

        bytes = atol(line_array[POS_BYTES]);

        /* primeira ocurrencia */
        if (!current->date)
            current->date = date;

        /* nova data */
        if (current->date != date) {
            current->next = (struct datestats *)malloc(sizeof(struct datestats));
            current = current->next;
            current->write_ops = current->write_bytes = 0;
            current->read_ops = current->read_bytes = 0;
            current->date = date;
            current->next = NULL;
        }

        /* incrementar counters */
        if (type == REQUEST) {
            current->write_ops++;
            current->write_bytes += bytes;
            write_bytes += bytes;
        } else {
            current->read_ops++;
            current->read_bytes += bytes;
            read_bytes += bytes;
        }

    }

    /* dump stats */
    uint64_t nops = 0;

    printf("Number of NFSOps REQUESTS:\n");
    for (i = 0; ops[i].opname != NULL; i++) {
        if (ops[i].request_count) {
            printf("%s:   \t%ld\n", ops[i].opname, ops[i].request_count);
            nops += ops[i].request_count;
        }
    }

    printf("\nNumber of NFSOps RESPONSES:\n");
    for (i = 0; ops[i].opname != NULL; i++)
        if (ops[i].response_count) printf("%s:   \t%ld\n",
                ops[i].opname, ops[i].response_count);
    

    printf("\nTotal # of NFSOPs: \t%ld\n", nops);

    printf("Data Read (Gb):   \t%.2f Gb\n", (double)(read_bytes >> 20)/1024);
    printf("Data Written (Gb):\t%.2f Gb\n", (double)(write_bytes >> 20)/1024);
    printf("Read/Write bytes ratio:\t%.2fx\n",
            (double)read_bytes/(double)write_bytes);
    printf("Read/Write ops ratio:\t%.2fx\n",
            (double)ops[OP_READ].request_count/(double)ops[OP_WRITE].request_count);

    struct tm *tmp;
    char outstr[11];

    printf("\nNumber of Read Operations:\n");
    current = head;
    while (current) {
        tmp = localtime(&(current->date));
        if (strftime(outstr, sizeof(outstr), "%Y%m%d%H", tmp) == 0) {
               fprintf(stderr, "strftime returned 0");
               exit(EXIT_FAILURE);
        }
        printf("%s, %ld\n", outstr, current->read_ops);
        old = current;
        current = old->next;
    }

    printf("\nNumber of Read Mbytes:\n");
    current = head;
    while (current) {
        tmp = localtime(&(current->date));
        if (strftime(outstr, sizeof(outstr), "%Y%m%d%H", tmp) == 0) {
               fprintf(stderr, "strftime returned 0");
               exit(EXIT_FAILURE);
        }
        printf("%s, %.2f\n", outstr, (double)(current->read_bytes >> 10)/1024);
        old = current;
        current = old->next;
    }

    printf("\nNumber of Write Operations:\n");
    current = head;
    while (current) {
        tmp = localtime(&(current->date));
        if (strftime(outstr, sizeof(outstr), "%Y%m%d%H", tmp) == 0) {
               fprintf(stderr, "strftime returned 0");
               exit(EXIT_FAILURE);
        }
        printf("%s, %ld\n", outstr, current->write_ops);
        old = current;
        current = old->next;
    }

    printf("\nNumber of Write Mbytes:\n");
    current = head;
    while (current) {
        tmp = localtime(&(current->date));
        if (strftime(outstr, sizeof(outstr), "%Y%m%d%H", tmp) == 0) {
               fprintf(stderr, "strftime returned 0");
               exit(EXIT_FAILURE);
        }
        printf("%s, %.2f\n", outstr, (double)(current->write_bytes >> 10)/1024);
        old = current;
        current = old->next;
    }

    printf("\n");
    
    exit(EXIT_SUCCESS);
    /* main_here */
}


