const Pack = require('./pack');
const PackEnum = require('./packenum');
const MapValue = require('../value/map-value');
const DataOutputX = require('../io/data-outputx');
const DataInputX = require('../io/data-inputx');
const HashUtil = require('../util/hashutil');
const BooleanValue = require('../value/boolean-value');

function LogSinkPack() {
    Pack.call(this);
    this.Category = '';
    this.time = null;
    this.tagHash = 0;
    this.tags = new MapValue();
    this.line = 0;
    this.content = '';
    this.fields = new MapValue();
}

LogSinkPack.prototype = Object.create(Pack.prototype);
LogSinkPack.prototype.constructor = LogSinkPack;

LogSinkPack.prototype.getPackType = function () {
    return PackEnum.LOGSINK;
};

LogSinkPack.prototype.toString = function () {
    return `${Pack.prototype.toString.call(this)} [category=${this.Category}, tagHash=${this.tagHash}, tags=${this.tags}, content=${this.content}, fields=${this.fields}]`;
};

LogSinkPack.prototype.getTagAsBytes = function () {
    const out = new DataOutputX();
    out.writeValue(this.tags);
    return out.toByteArray();
};

LogSinkPack.prototype.getContentBytes = function () {
    const o = new DataOutputX();
    o.writeByte(1);
    o.writeText(this.content);
    o.writeDecimal(this.line);
    return o.toByteArray();
};

LogSinkPack.prototype.putCtr = function (key, value) {
    if (key.charAt(0) === '!') {
        this.fields.putValue(key, value);
    }
};

LogSinkPack.prototype.getCtr = function (key) {
    return this.fields.get(key);
};

LogSinkPack.prototype.getCtrBoolean = function (key) {
    const v = this.fields.get(key);
    if (v instanceof BooleanValue) {
        return v.value;
    }
    return false;
};

LogSinkPack.prototype.content = function () {
    return this.content || '';
};

LogSinkPack.prototype.setContent = function (str) {
    this.content = str;
};

LogSinkPack.prototype.setContentBytes = function (d) {
    try {
        if (!d || d.length < 1) return;
        const inStream = new DataInputX(d);
        const ver = inStream.readByte();
        if (ver === 1) {
            this.content = inStream.readText();
            this.line = inStream.readDecimal();
        }
    } catch (e) {
        // Handle error
    }
};

LogSinkPack.prototype.write = function (dout) {
    Pack.prototype.write.call(this, dout);

    dout.writeByte(0);
    dout.writeText(this.Category);

    if (this.tagHash === 0 && this.tags.size() > 0) {
        const result = this.resetTagHash();
        this.tagHash = result.tagHash;
        dout.writeDecimal(result.tagHash);
        dout.write(result.tagBytes);
    } else {
        dout.writeDecimal(this.tagHash);
        dout.writeValue(this.tags);
    }

    dout.writeDecimal(this.line);
    dout.writeText(this.content);

    if (this.fields && this.fields.size() > 0) {
        dout.writeBoolean(true);
        dout.writeValue(this.fields);
    } else {
        dout.writeBoolean(false);
    }
};

LogSinkPack.prototype.read = function (din) {
    Pack.prototype.read.call(this, din);
    const ver = din.readByte();
    this.Category = din.readText();
    this.tagHash = din.readDecimal();
    this.tags = din.readValue();
    this.line = din.readDecimal();
    this.content = din.readText();
    if (din.readBoolean()) {
        this.fields = din.readValue();
    }
    return this;
};

LogSinkPack.prototype.resetTagHash = function () {
    const dout = new DataOutputX();
    dout.writeValue(this.tags);
    const tagBytes = dout.toByteArray();
    const tagHash = HashUtil.hash(tagBytes);
    return { tagHash: tagHash, tagBytes: tagBytes };
};

LogSinkPack.prototype.transferOidToTag = function () {
    if (this.oid !== 0 && !this.tags.containsKey('oid')) {
        this.tags.putValue('oid', this.oid);
        this.tagHash = 0;
    }
    if (this.okind !== 0 && !this.tags.containsKey('okind')) {
        this.tags.putValue('okind', this.okind);
        this.tagHash = 0;
    }
    if (this.onode !== 0 && !this.tags.containsKey('onode')) {
        this.tags.putValue('onode', this.onode);
        this.tagHash = 0;
    }
};

LogSinkPack.getLogSinkPack = function(options) {
    const {
        t = 0,
        category = null,
        tags = {},
        fields = {},
        line = 0,
        content = null
    } = options || {};

    const pack = new LogSinkPack();
    pack.time = t;
    pack.Category = category;

    for (const [k, v] of Object.entries(tags)) {
        pack.tags.putString(k, String(v));
    }

    for (const [k, v] of Object.entries(fields)) {
        pack.fields.putString(k, String(v));
    }

    pack.line = line;
    pack.content = content;

    return pack;
};

module.exports = LogSinkPack;