/*
 * Copyright (c) 2013-2017 Red Hat.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

#include "metrics.h"

metric_t *
find_metric(char *name)
{
    metric_t *m;

    for (m = metrics; m->name; m++) {
    	if (strcmp(name, m->name) == 0)
	    break;
    }

    return m->name ? m : NULL;
}

handler_t *
find_handler(char *tag)
{
    handler_t *h;

    for (h = handlers; h->pattern; h++) {
	if (strchr(h->pattern, '*') != NULL) {
	    /* match tag as a prefix, e.g. cpu* matches cpu123 */
	    if (strncmp(h->pattern, tag, strlen(h->pattern)-1) == 0)
		break;
	}
	else {
	    /* exact match */
	    if (strcmp(h->pattern, tag) == 0)
		break;
	}
    }

    return h->pattern ? h : NULL;
}

int
put_str_instance(pmInDom indom, char *instance)
{
    int sts;
    int id;

    if (pmInDom_domain(indom) == PROC_DOMAIN)
    	id = atoi(instance);
    else
	id = indom_cnt[pmInDom_serial(indom)]++;

    sts = pmiAddInstance(indom, instance, id);
    return sts ? sts : id;
}

int
put_str_value(char *name, pmInDom indom, char *instance, char *val)
{
    int sts = 0;

    if (!val)
	fprintf(stderr, "Warning: put_str_value: ignored NULL value for \"%s\"\n", name);
    else if (indom != PM_INDOM_NULL && !instance)
	fprintf(stderr, "Warning: put_str_value: ignored NULL instance for non-singular indom for \"%s\"\n", name);
    else {
	sts = pmiPutValue(name, instance, val);
	if (sts == PM_ERR_NAME) {
	    if (vflag)
		fprintf(stderr, "Warning: unknown metric name \"%s\". Check metrics.c\n", name);
	    return sts;
	}
	if (indom != PM_INDOM_NULL && instance && (sts == PM_ERR_INST || sts == PM_ERR_INDOM)) {
	    /* New instance has appeared */
	    sts = put_str_instance(indom, instance);
	    if (sts < 0)
		fprintf(stderr, "Warning: put_str_value failed to add new instance \"%s\" for indom:%d err:%d : %s\n",
		    instance, indom, sts, pmiErrStr(sts));
	    else if (vflag)
		printf("New instance %s[%d] \"%s\"\n", name, sts, instance);
	    sts = pmiPutValue(name, instance, val);
	    if (sts < 0)
		fprintf(stderr, "Warning: put_str_value \"%s\" inst:\"%s\" value:\"%s\" failed: err=%d %s\n",
		    name, instance, val, sts, pmiErrStr(sts));
	}
    }

    return sts;
}

int
put_int_value(char *name, pmInDom indom, char *instance, int val)
{
    char valbuf[64];

    pmsprintf(valbuf, sizeof(valbuf), "%d", val);
    return put_str_value(name, indom, instance, valbuf);
}

int
put_ull_value(char *name, pmInDom indom, char *instance, unsigned long long val)
{
    char valbuf[64];

    pmsprintf(valbuf, sizeof(valbuf), "%" FMT_UINT64, (__uint64_t)val);
    return put_str_value(name, indom, instance, valbuf);
}

/* split a string into fields and their lengths. Free fields[0] when done. */
int
strfields(const char *s, int len, char **fields, int *fieldlen, int maxfields)
{
    int i;
    char *p;
    char *p_end;

    if (!s || *s == '\0')
    	return 0;
    
    if ((p = strdup(s)) == NULL)
    	return 0;

    for (i=0, p_end = p+len; i < maxfields;) {
        fields[i] = p;
	fieldlen[i] = 0;
        while(*p && !isspace((int)*p) && p < p_end) {
            p++;
	    fieldlen[i]++;
	}
	i++;
	if (!*p)
	    break;
        *p++ ='\0';
	while (isspace((int)*p))
	    p++;
    }

    return i;
}

fields_t *
fields_new(const char *s, int len)
{
    int n = 1;
    const char *p;
    fields_t *f = (fields_t *)malloc(sizeof(fields_t));

    memset(f, 0, sizeof(fields_t));
    f->len = len;
    for (p=s; *p && p < s+len; p++) {
	if (isspace((int)*p))
	    n++;
    }
    /*
     * n is an upper bound, at least 1 (separator may repeat).
     * fields[0] is the actual buffer, allocated by strfields
     */
    f->fields = (char **)malloc(n * sizeof(char *));
    f->fieldlen = (int *)malloc(n * sizeof(int));
    f->nfields = strfields(s, len, f->fields, f->fieldlen, n);
    f->buf = f->fields[0];

    return f;
}

fields_t *
fields_dup(fields_t *f)
{
    int i;
    fields_t *copy;

    copy = malloc(sizeof(fields_t));
    memset(copy, 0, sizeof(fields_t));
    copy->len = f->len;
    copy->buf = (char *)malloc(f->len + 1);
    memcpy(copy->buf, f->buf, f->len);

    copy->nfields = f->nfields;
    copy->fields = (char **)malloc(f->nfields * sizeof(char *));
    copy->fieldlen = (int *)malloc(f->nfields * sizeof(int));

    copy->fields[0] = copy->buf;
    for (i=1; i < f->nfields; i++) {
    	copy->fieldlen[i] = f->fieldlen[i];
    	copy->fields[i] = copy->fields[0] + (f->fields[i] - f->fields[0]);
    }

    return copy;
}

void
fields_free(fields_t *f)
{
    free(f->buf);
    free(f->fields);
    free(f->fieldlen);
    free(f);
}

