/*
 * Serializer to and from Juttle/Influx points
 */

var JuttleMoment = require('../../juttle/lib/moment').JuttleMoment;
var _ = require('underscore');

var Serializer = function(options) {
    this.options = options || {};

    this.intFields = this.options.intFields ? this.options.intFields.split(',') : [];
    this.valFields = this.options.valFields ? this.options.valFields.split(',') : [];
    this.measurementField = this.options.measurementField;
};

Serializer.prototype.toJuttle = function(measurement, keys, values) {
    if (!measurement) { throw new Error('measurement argument required'); }
    if (!keys) { throw new Error('keys argument required'); }
    if (!values) { throw new Error('values argument required'); }

    // FIXME: our sinks can't handle nulls?
    var obj  = _.object(keys, _.map(values, function(v) { return (v === null ? '' : v); }));

    if (this.measurementField) {
        obj[this.measurementField] = measurement;
    }

    if (obj.time) {
        obj.time = JuttleMoment.parse(obj.time);
    }

    return obj;
};

Serializer.prototype.toInflux = function(point) {
    var self = this;

    var measurement = point[this.measurementField];
    var timestamp   = point.time ? point.time.unixms() + '' : '';

    var point = _.omit(point, this.measurementField, 'time');

    var tags = _.pick(point, this._isTag.bind(this));
    var vals = _.omit(point, this._isTag.bind(this));

    if (_.keys(vals).length == 0) { throw new Error('point requires at least one field'); }
    if (!measurement) { throw new Error('point is missing a measurement'); }

    var key = _.flatten([measurement, _.map(tags, this._concatKeyVal)]).map(this._escapeTag);

    var val = _.chain(vals)
               .map(this._serializeValue.bind(this))
               .object()
               .map(this._concatKeyVal)
               .value();

    return _.reject([key.join(","), val.join(","), timestamp], _.isEmpty).join(" ");
};

Serializer.prototype._concatKeyVal = function(v, k)
{
    return k + '=' + v;
}

Serializer.prototype._serializeValue = function(v, k)
{
    if (_.isBoolean(v)) {
        return [k, (v ? 't' : 'f')];
    } else
    if (_.isString(v)) {
        return [k, '"' + this._escapeString(v) + '"'];
    } else
    if (this._isInt(v, k)) {
        return [k, Math.floor(v) + 'i'];
    } else
    if (v instanceof JuttleMoment) {
        return [k, v.unixms()];
    } else {
        return [k, v];
    }
}

// https://influxdb.com/docs/v0.9/write_protocols/line.html#key
Serializer.prototype._escapeTag = function(str) {
   var specialChars = /[\s,]/g;
   return str.replace(specialChars, function(m) { return "\\" + m; });
}

// https://influxdb.com/docs/v0.9/write_protocols/line.html#fields (Strings)
Serializer.prototype._escapeString = function(str) {
    return str.replace(/"/g, '\\"');
};

Serializer.prototype._isTag = function(val, key, obj) {
    return _.isString(val) && !_.contains(this.valFields, key);
}

Serializer.prototype._isInt = function(val, key, obj) {
    return _.isNumber(val) && _.contains(this.intFields, key);
}

module.exports = Serializer;

@@ -1237,17 +1237,14 @@
 s() 
-+ '' : ''
+: null
 ;%0A%0A 
@@ -1868,12 +1868,13 @@
 n _.
-reje
+compa
 ct(%5B
@@ -1917,19 +1917,8 @@
 amp%5D
-, _.isEmpty
 ).jo

Use compact instead of reject + isEmpty