#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <errno.h>

#include "libclamav/others.h"
#include "libclamav/clamav.h"

#define JSON_BUFSZ 512
#define SAMPLE_PREFIX "sample_"

char *hex_encode(char *buf, char *data, size_t len)
{
    size_t i;
    char *p;

    p = (buf != NULL) ? buf : calloc(1, (len*2)+1);
    if (!(p))
        return NULL;

    for (i=0; i<len; i++)
        sprintf(p+(i*2), "%02x", *(int *)(data+i) & 0xff);

    return p;
}

const char *get_sample_type(cli_intel_sample_type_t type)
{
    switch (type) {
        case WHOLEFILE:
            return "whole-file";
        case PESECTION:
            return "PE section";
        default:
            return NULL;
    }
}

char *ensure_bufsize(char *buf, size_t *oldsize, size_t used, size_t additional)
{
    char *p=buf;

    if (*oldsize - used < additional) {
        p = realloc(buf, *oldsize + JSON_BUFSZ);
        if (!(p)) {
            cli_errmsg("ensure_bufsize: Could not allocate more memory: %s (errno: %d)\n", strerror(errno), errno);
            free(buf);
            return NULL;
        }

        *oldsize += JSON_BUFSZ;
    }

    return p;
}

char *export_stats_to_json(struct cl_engine *engine, cli_intel_t *intel)
{
    char *buf=NULL, *p, *hostid, md5[33];
    const char *type;
    cli_flagged_sample_t *sample;
    size_t bufsz, curused, i;

    if (!(intel->hostid))
        if ((engine->cb_stats_get_hostid))
            intel->hostid = engine->cb_stats_get_hostid(engine->stats_data);

    hostid = (intel->hostid != NULL) ? intel->hostid : STATS_ANON_UUID;

    buf = calloc(1, JSON_BUFSZ);
    if (!(buf))
        return NULL;

    bufsz = JSON_BUFSZ;
    sprintf(buf, "{\n\t\"hostid\": \"%s\",\n", hostid);
    sprintf(buf+strlen(buf), "\t\"samples\": {\n");
    curused = strlen(buf);

    for (sample = intel->samples; sample != NULL; sample = sample->next) {
        if (sample->hits == 0)
            continue;

        memset(md5, 0x00, sizeof(md5));
        hex_encode(md5, sample->md5, sizeof(sample->md5));

        buf = ensure_bufsize(buf, &bufsz, curused, strlen(md5) + sizeof(SAMPLE_PREFIX) + 45);
        if (!(buf))
            return NULL;

        snprintf(buf+curused, bufsz-curused, "\t\t\"%s%s\": {\n", SAMPLE_PREFIX, md5);
        curused += strlen(buf+curused);

        type = get_sample_type(sample->type);
        if (!(type)) {
            free(buf);
            return NULL;
        }

        buf = ensure_bufsize(buf, &bufsz, curused, sizeof("type") + strlen(type) + 15);
        if (!(buf))
            return NULL;

        snprintf(buf+curused, bufsz-curused, "\t\t\t\"type\": \"%s\",\n", type);
        curused += strlen(buf+curused);

        /* Reuse the md5 variable for serializing the number of hits */
        snprintf(md5, sizeof(md5), "%u", sample->hits);

        buf = ensure_bufsize(buf, &bufsz, curused, strlen(md5) + 20);
        if (!(buf))
            return NULL;

        snprintf(buf+curused, bufsz-curused, "\t\t\t\"size\": \"%s\",\n", md5);
        curused += strlen(buf+curused);

        buf = ensure_bufsize(buf, &bufsz, curused, 30);
        if (!(buf))
            return NULL;

        snprintf(buf+curused, bufsz-curused, "\t\t\t\"virus_names\": [ ");
        curused += strlen(buf+curused);

        for (i=0; sample->virus_name[i] != NULL; i++) {
            buf = ensure_bufsize(buf, &bufsz, curused, strlen(sample->virus_name[i]) + 5);
            if (!(buf))
                return NULL;

            snprintf(buf+curused, bufsz-curused, "%s\"%s\"", (i > 0) ? ", " : "", sample->virus_name[i]);
            curused += strlen(buf+curused);
        }

        buf = ensure_bufsize(buf, &bufsz, curused, 10);
        if (!(buf))
            return NULL;

        snprintf(buf+curused, bufsz-curused, " ]\n\t\t}%s\n", (sample->next != NULL) ? "," : "");
        curused += strlen(buf+curused);
    }

    buf = ensure_bufsize(buf, &bufsz, curused, 15);
    if (!(buf))
        return NULL;

    snprintf(buf+curused, bufsz-curused, "\t}\n}\n");

    return buf;
}