/**
 * Copyright 2016 the WHATAP project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */

var Pack = require('./pack'),
    PackEnum = require('./packenum'),
    DataOutputX = require('./../io/data-outputx'),
    DataInputX = require('./../io/data-inputx'),
    IntMapValue = require('./../value/int-map-value'),
    IntValue = require('./../value/int-value'),
    FloatValue = require('./../value/float-value'),
    IntIntMap = require('../util/intint-map'),
    IntKeyLinkedMap = require('../util/intkey-linkedmap'),
    LongKeyLinkedMap = require('../util/longkey-linkedmap'),
    Long = require('long');

function CounterPack() {
    Pack.call(this);
    this.duration = 0; // sec
    this.cputime = 0;

    this.heap_tot = 0;
    this.heap_use = 0;
    this.heap_perm = 0;
    this.heap_pending_finalization = 0;

    this.gc_count = 0;
    this.gc_time = 0;

    this.service_count = 0;
    this.service_error = 0;
    this.service_time = 0;

    this.sql_count = 0;
    this.sql_error = 0;
    this.sql_time = 0;

    this.sql_fetch_count = 0;
    this.sql_fetch_time = 0;

    this.httpc_count = 0;
    this.httpc_error = 0;
    this.httpc_time = 0;

    this.active_tx_count = 0;
    this.active_tx_slice = new Array(3).fill(0);

    this.cpu = 0;
    this.cpu_sys = 0;
    this.cpu_usr = 0;
    this.cpu_wait = 0;
    this.cpu_steal = 0;
    this.cpu_irq = 0;

    this.cpu_proc = 0;
    this.cpu_cores = 0;

    this.mem = 0;
    this.swap = 0;
    this.disk = 0;

    this.thread_total_started = 0;
    this.thread_count = 0;
    this.thread_daemon = 0;
    this.thread_peak_count = 0;

    this.starttime = 0;
    this.pack_dropped = 0;

    this.db_num_active = null;
    this.db_num_idle = null;

    this.host_ip = 0;
    this.proc_fd = 0;
    this.tps = 0;
    this.resp_time = 0;

    this.apType = 2;
    this.websocket = null;

    /*추가항목들..20180717 */
    this.mac_hash = 0;
    this.extra = null; // IntMapValue

    this.pid = 0;
    //{"method","sql","httpc", "dbc","socket"};
    this.active_stat = null;

    this.threadpool_activeCount = 0;
    this.threadpool_queueSize = 0;

    this.txcaller_oid_meter = null;
    this.sql_meter = null;
    this.httpc_meter = null;
    this.txcaller_group_meter = null;
    this.txcaller_unknown = null;

    this.containerKey = 0;

    this.tx_dbc_time = 0;//avg time
	this.tx_sql_time = 0;//avg time
	this.tx_httpc_time = 0;//avg time

    this.apdex_satisfied = 0;
	this.apdex_tolerated = 0;
    this.apdex_total = 0;
    
    this.arrival_rate = 0;
    this.gc_oldgen_count = 0;
    this.version = 2;
    this.heap_max = 0;

    this.proc_fd_max = 0;

    this.metering = 0;
}
CounterPack.prototype = new Pack();
CounterPack.prototype.constructor = CounterPack;
CounterPack.prototype.getPackType = function () {
    return PackEnum.COUNTER_1;
};
CounterPack.prototype.write = function (o) {
    Pack.prototype.write.call(this, o);

    var dout = new DataOutputX();
    dout.writeDecimal(this.duration);
    dout.writeDecimal(this.cputime);

    dout.writeDecimal(this.heap_tot);
    dout.writeDecimal(this.heap_use);
    dout.writeDecimal(this.heap_perm);
    dout.writeDecimal(this.heap_pending_finalization);

    dout.writeDecimal(this.gc_count);
    dout.writeDecimal(this.gc_time);

    dout.writeDecimal(this.service_count);
    dout.writeDecimal(this.service_error);
    dout.writeDecimal(this.service_time);

    dout.writeDecimal(this.sql_count);
    dout.writeDecimal(this.sql_error);
    dout.writeDecimal(this.sql_time);
    dout.writeDecimal(this.sql_fetch_count);
    dout.writeDecimal(this.sql_fetch_time);

    dout.writeDecimal(this.httpc_count);
    dout.writeDecimal(this.httpc_error);
    dout.writeDecimal(this.httpc_time);

    dout.writeDecimal(this.active_tx_count);
    CounterPack.writeShortArray(dout, this.active_tx_slice);

    dout.writeFloat(this.cpu);
    dout.writeFloat(this.cpu_sys);
    dout.writeFloat(this.cpu_usr);
    dout.writeFloat(this.cpu_wait);
    dout.writeFloat(this.cpu_steal);
    dout.writeFloat(this.cpu_irq);

    dout.writeFloat(this.cpu_proc);
    dout.writeDecimal(this.cpu_cores);
    dout.writeFloat(this.mem);
    dout.writeFloat(this.swap);
    dout.writeFloat(this.disk);

    dout.writeDecimal(this.thread_total_started);
    dout.writeDecimal(this.thread_count);
    dout.writeDecimal(this.thread_daemon);
    dout.writeDecimal(this.thread_peak_count);


    var db = this.db_num_active != null && this._idle != null;
    dout.writeBoolean(db);
    if (db) {
        this.db_num_active.toBytes(dout);
        this.db_num_idle.toBytes(dout);
    }

    if (this.netstat == null) {
        dout.writeBoolean(false);
    } else {
        dout.writeBoolean(true);
        dout.writeDecimal(this.netstat.est);
        dout.writeDecimal(this.netstat.fin_w);
        dout.writeDecimal(this.netstat.clo_w);
        dout.writeDecimal(this.netstat.tim_w);
    }

    dout.writeDecimal(this.proc_fd);
    dout.writeFloat(this.tps);
    dout.writeDecimal(this.resp_time);

    dout.writeShort(this.apType);

    if (this.websocket == null) {
        dout.writeByte(0);
    } else {
        dout.writeByte(1);
        dout.writeDecimal(this.websocket.count);
        dout.writeDecimal(this.websocket.in);
        dout.writeDecimal(this.websocket.out);
    }

    dout.writeDecimal(this.starttime);
    dout.writeDecimal(this.pack_dropped);
    dout.writeDecimal(this.host_ip);


    dout.writeDecimal(this.mac_hash);

    if (this.extra == null || this.extra.size() === 0) {
        dout.writeByte(0);
    } else {
        dout.writeByte(1);
        dout.writeValue(this.extra);
    }
    dout.writeInt(this.pid);
    var sz = this.active_stat == null ? 0 : this.active_stat.length;
    dout.writeByte(sz);
    for (var i = 0; i < sz; i++) {
        dout.writeShort(this.active_stat[i]);
    }
    dout.writeDecimal(this.threadpool_activeCount);
    dout.writeDecimal(this.threadpool_queueSize);

    write_meter(dout, this.txcaller_oid_meter);
    write_meter_sql(dout, this.sql_meter);
    write_meter(dout, this.httpc_meter);
    write_meter_long(dout, this.txcaller_group_meter);
    dout.writeDecimal(0);
    write_meter_other(dout, this.txcaller_unknown);

    dout.writeDecimal(this.containerKey);
		
    dout.writeFloat(this.tx_dbc_time);
    dout.writeFloat(this.tx_sql_time);
    dout.writeFloat(this.tx_httpc_time);
    
    dout.writeDecimal(this.apdex_satisfied);
    dout.writeDecimal(this.apdex_tolerated);

    dout.writeFloat(this.arrival_rate);
    
    dout.writeDecimal(this.gc_oldgen_count);
    dout.writeByte(this.version);
    dout.writeDecimal(this.heap_max);
    
    dout.writeDecimal(this.proc_fd_max);
    dout.writeFloat(this.metering);

    dout.writeDecimal(this.apdex_total);

    o.writeBlob(dout.toByteArray());
};

function write_meter_other(dout, meter) {
    if (meter != null && meter.count > 0) {
        dout.writeByte(2);
        dout.writeDecimal(meter.time);
        dout.writeDecimal(meter.count);
        dout.writeDecimal(meter.error);
        dout.writeDecimal(meter.actx);
    } else {
        dout.writeByte(0);
    }
}

function write_meter(dout, meter) {
    if (meter == null || meter.size() == 0) {
        dout.writeDecimal(0);
    } else {
        dout.writeByte(9);
        dout.writeDecimal(meter.size());
        var en = meter.entries();
        while (en.hasMoreElements()) {
            var ent = en.nextElement();
            var m = ent.getValue();

            dout.writeInt(ent.getKey());
            dout.writeDecimal(m.time);
            dout.writeDecimal(m.count);
            dout.writeDecimal(m.error);
            dout.writeDecimal(m.actx); 
        }
    }
}
function write_meter_sql(dout, meter) {
    if (meter == null || meter.size() == 0) {
        dout.writeDecimal(0);
    } else {
        dout.writeByte(9);
        dout.writeDecimal(meter.size());
        var en = meter.entries();
        while (en.hasMoreElements()) {
            var ent = en.nextElement();
            var m = ent.getValue();

            dout.writeInt(ent.getKey());
            dout.writeDecimal(m.time);
            dout.writeDecimal(m.count);
            dout.writeDecimal(m.error);
            dout.writeDecimal(m.actx); //m.actx
             dout.writeDecimal(m.fetch_count);
                dout.writeDecimal(m.fetch_time);
        }
    }
}

function write_meter_long(dout, meter) {
    if (meter == null || meter.size() == 0) {
        dout.writeDecimal(0);
    } else {
        dout.writeByte(9);
        dout.writeDecimal(meter.size());
        var en = meter.entries();
        while (en.hasMoreElements()) {
            var ent = en.nextElement();
            var m = ent.getValue();

            var pkind = ent.getKey();
            dout.writeDecimal(pkind.pcode);
            dout.writeDecimal(pkind.okind);
            dout.writeDecimal(m.time);
            dout.writeDecimal(m.count);
            dout.writeDecimal(m.error);
            dout.writeDecimal(m.actx);
        }
    }
}
CounterPack.prototype.read = function (io) {
    Pack.prototype.read.call(this, io);

    var din = new DataInputX(io.readBlob());
    this.duration = din.readDecNumber();
    this.cputime = din.readDecNumber();
    this.heap_tot = din.readDecNumber();
    this.heap_use = din.readDecNumber();
    this.heap_perm = din.readDecNumber();
    this.heap_pending_finalization = din.readDecNumber();

    this.gc_count = din.readDecNumber();
    this.gc_time = din.readDecNumber();

    this.service_count = din.readDecNumber();
    this.service_error = din.readDecNumber();
    this.service_time = din.readDecNumber();

    this.sql_count = din.readDecNumber();
    this.sql_error = din.readDecNumber();
    this.sql_time = din.readDecNumber();
    this.sql_fetch_count = din.readDecNumber();
    this.sql_fetch_time = din.readDecNumber();

    this.httpc_count = din.readDecNumber();
    this.httpc_error = din.readDecNumber();
    this.httpc_time = din.readDecNumber();

    this.active_tx_count = din.readDecNumber();
    this.active_tx_slice = CounterPack.readShortArray(din);

    this.cpu = din.readFloat();
    this.cpu_sys = din.readFloat();
    this.cpu_usr = din.readFloat();
    this.cpu_wait = din.readFloat();
    this.cpu_steal = din.readFloat();
    this.cpu_irq = din.readFloat();

    this.cpu_proc = din.readFloat();
    this.cpu_cores = din.readDecimal();

    this.mem = din.readFloat();
    this.swap = din.readFloat();
    this.disk = din.readFloat();

    this.thread_total_started = din.readDecimal();
    this.thread_count = din.readDecimal();
    this.thread_daemon = din.readDecimal();
    this.thread_peak_count = din.readDecimal();

    if (din.readBoolean() == true) {
        this.db_num_active = new IntIntMap();
        this.db_num_idle = new IntIntMap();
        this.db_num_active.toObject(din);
        this.db_num_idle.toObject(din);
    }
    if (din.readBoolean() == true) {
        this.netstat = new NETSTAT();
        this.netstat.est = din.readDecNumber();
        this.netstat.fin_w = din.readDecNumber();
        this.netstat.clo_w = din.readDecNumber();
        this.netstat.tim_w = din.readDecNumber();
    }

    this.proc_fd = din.readDecNumber();
    this.tps = din.readFloat();
    this.resp_time = din.readDecNumber();

    this.apType = din.readShort();

    if (din.readBoolean() === true) {
        this.websocket = new WEBSOCKET();
        this.websocket.count = din.readDecNumber();
        this.websocket.in = din.readDecNumber();
        this.websocket.out = din.readDecNumber();
    }

    this.starttime = din.readDecNumber();
    this.pack_dropped = din.readDecNumber();
    this.host_ip = din.readDecNumber();

    this.mac_hash = din.readDecNumber();

    if (din.readByte() === 1) {
        this.extra = din.readValue();
    }

    this.pid = din.readInt();

    var sz = din.readByte();
    this.active_stat = new Array(sz);
    for (var i = 0; i < sz; i++) {
        this.active_stat[i] = din.readShort();
    }
    this.threadpool_activeCount = din.readDecNumber();
    this.threadpool_queueSize = din.readDecNumber();

    this.txcaller_oid_meter = read_meter(din);
    this.sql_meter = read_meter_sql(din);
    this.httpc_meter = read_meter(din);
    this.txcaller_group_meter = read_meter_long(din);
    read_meter(din);
    this.txcaller_unknown = read_meter_unknown(din);

    if (din.available() > 0) {
        this.containerKey = din.readDecimal();
    }

    if (din.available() > 0) {
        this.tx_dbc_time = din.readFloat();
        this.tx_sql_time = din.readFloat();
        this.tx_httpc_time = din.readFloat();
    }
    if (din.available() > 0) {
        this.apdex_satisfied = din.readDecimal();
        this.apdex_tolerated = din.readDecimal();
    }
    
    if (din.available() > 0) {
        this.arrival_rate = din.readFloat();
    }
    if (din.available() > 0) {
        this.gc_oldgen_count = din.readDecimal();
        this.version=din.readByte();
        this.heap_max=din.readDecimal();
    }
    if (din.available() > 0) {
        this.proc_fd_max = din.readDecimal();
    }
    if (din.available() > 0) {
        this.metering =  din.readFloat();
    }
    if (din.available() > 0) {
        this.apdex_total = din.readDecimal();
    }

    return this;
};

function read_meter_unknown(din) {
    var ver = din.readByte();
    if (ver > 0) {
        var time = din.readDecNumber();
        var count = din.readDecNumber();
        var error = din.readDecNumber();
        var actx = 0;
        if (ver >= 2) {
            actx = din.readDecNumber();
        }
        return {
            time: time,
            count: count,
            error: error,
            actx: actx
        };
    } else {
        return null;
    }
}

function read_meter(din) {
    var ver = din.readByte();
    if (ver == 0)
        return null;
    var n;
    if (ver <= 8) {
        n = din.readDecNumber(ver);
    } else {
        n = din.readDecNumber();
    }
    var meter = new IntKeyLinkedMap();
    for (var i = 0; i < n; i++) {
        var key = din.readInt();
        var time = din.readDecNumber();
        var count = din.readDecNumber();
        var error = din.readDecNumber();
        var actx = 0;
        if (ver >= 9) {
            actx = din.readDecNumber();
        }
        meter.put(key, {
            time: time,
            count: count,
            error: error,
            actx: actx
        });
    }
    return meter;

}

function read_meter_long(din) {
    var ver = din.readByte();
    if (ver == 0)
        return null;
    var n;
    if (ver <= 8) {
        n = din.readDecNumber(ver);
    } else {
        n = din.readDecNumber();
    }
    var meter = new LinkedMap();
    for (var i = 0; i < n; i++) {
        var pcode = din.readDecNumber();
        var okind = 0;
        if (ver >= 9) {
            din.readDecNumber();
        }
        var time = din.readDecNumber();
        var count = din.readDecNumber();
        var error = din.readDecNumber();
        var actx = 0;
        if (ver >= 9) {
            actx = din.readDecNumber();
        }
        meter.put({
            pcode: pcode,
            okind: okind
        }, {
            time: time,
            count: count,
            error: error,
            actx: actx
        });
    }
    return meter;

}

function read_meter_sql(din) {
    var ver = din.readByte();
    if (ver == 0)
        return null;
    var n;
    if (ver <= 8) {
        n = din.readDecNumber(ver);
    } else {
        n = din.readDecNumber();
    }
    var meter = new IntKeyLinkedMap();
    for (var i = 0; i < n; i++) {
        var sql = din.readInt();
        var time = din.readDecNumber();
        var count = din.readDecNumber();
        var error = din.readDecNumber();
        var actx = 0;
        if (ver >= 9) {
            actx = din.readDecNumber();
        }
        var fetch_count = din.readDecNumber();
        var fetch_time = din.readDecNumber();
        meter.put(sql, {
            time: time,
            count: count,
            error: error,
            actx: actx,
            fetch_count: fetch_count,
            fetch_time: fetch_time
        });
    }
    return meter;

}
CounterPack.writeShortArray = function (out, v) {
    if (v == null) {
        out.writeByte(0);
    } else {
        out.writeByte(v.length);
        for (var i = 0; i < v.length; i++) {
            out.writeShort(v[i]);
        }
    }
};
CounterPack.readShortArray = function (din) {
    var len = din.readByte();
    var arr = new Array(len);

    for (var i = 0; i < len; i++) {
        arr[i] = din.readShortBytes();
    }
    return arr;
};
CounterPack.prototype.toString = function () {
    var str = "CounterPack" + Pack.prototype.toString.call(this);
    str += "[";
    var keys = Object.keys(this);
    for (var i = 0; i < keys.length; i++) {
        if (i != 0) {
            str += ", ";
        }
        str += keys[i];
        str += "=";
        str += this[keys[i]]
    }
    str += "]";
    return str;
};
CounterPack.prototype.putFloat = function (cid, value) {
    if (typeof cid === 'number') {
        if (this.extra == null) {
            this.extra = new IntMapValue();
        }
        this.extra.putValue(cid, new FloatValue(value));
    }
}
CounterPack.prototype.putInt = function (cid, value) {
    if (typeof cid === 'number') {
        if (this.extra == null) {
            this.extra = new IntMapValue();
        }
        this.extra.putValue(cid, new IntValue(value));
    }
}

module.exports = CounterPack;