/**
 * 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 ValueEnum       = require('../value/valueenum'),
    PackEnum        = require('../pack/packenum'),
    Long            = require('long');

function DataInputX(inputBuf) {
    this.buf = Buffer.alloc(inputBuf.length);
    inputBuf.copy(this.buf);
    this.offset = 0;
}

DataInputX.prototype.size = function() {
    return this.buf.length;
};

DataInputX.prototype.available = function() {
    return (this.size() - this.offset) > 0;
};

DataInputX.prototype.readDecimal = function() {
    var len = this.readByte();
    switch (len) {
        case 0: return 0;
        case 1: return this.readInt8();
        case 2: return this.readUInt16BE();
        case 3: return this.readInt24BE();
        case 4: return this.readInt32BE();
        case 5: return this.readInt40BE();
        case 8: return this.readLong();
    }
    return 0;
};
DataInputX.prototype.readDecLong = function() {
    var len = this.readByte();
    switch (len) {
        case 0:
            return Long.ZERO;
        case 1:
            return Long.fromNumber(this.readInt8());
        case 2:
            return Long.fromNumber(this.readUInt16BE());
        case 3:
            return Long.fromNumber(this.readInt24BE());
        case 4:
            return Long.fromNumber(this.readInt32BE());
        case 5:
            return Long.fromNumber(this.readInt40BE());
        case 8:
            return this.readLong();
    }
    return 0;
};
DataInputX.prototype.readDecNumber = function() {
    var len = this.readByte();
    switch (len) {
        case 0: return 0;
        case 1: return this.readInt8();
        case 2: return this.readUInt16BE();
        case 3: return this.readInt24BE();
        case 4: return this.readInt32BE();
        case 5: return this.readInt40BE();
        case 8: return this.readLong().toNumber();
    }
    return 0;
};
DataInputX.prototype.readDecNumberLen = function(len) {
    switch (len) {
        case 0: return 0;
        case 1: return this.readInt8();
        case 2: return this.readUInt16BE();
        case 3: return this.readInt24BE();
        case 4: return this.readInt32BE();
        case 5: return this.readInt40BE();
        case 8: return this.readLong().toNumber();
    }
    return 0;
};

DataInputX.prototype.readBlob = function() {
    var baseLength = this.readUInt8();
    switch (baseLength) {
        case 255:
            var len = this.readInt16BE();
            return this.readSlice(len);
        case 254:
            var len = this.readInt32BE();
            return this.readSlice(len);
        case 0:
            return Buffer.alloc(0);
        default:
            return this.readSlice(baseLength);
    }
};

DataInputX.prototype.read = function(len) {
    return this.readSlice(len);
};

DataInputX.prototype.readSlice = function(len) {
    var slice = this.buf.slice(this.offset, this.offset + len);
    this.offset += len;
    return slice;
};

DataInputX.prototype.readText = function() {
    var blobBuf = this.readBlob();
    return blobBuf.toString("utf8", 0, blobBuf.length);
};

DataInputX.prototype.readByte = function() {
    var value = this.buf[this.offset];
    this.offset += 1;
    return value;
};

DataInputX.prototype.readIntBytes = function(max) {
    var len = this.readInt32BE();
    if(len < 0 || len > max){
        Logger.printError('WHATAP-501', "read byte is overflowed max:" + max + " len:" + len, new Error());
    }
    return this.read(len);
};

DataInputX.prototype.readShortBytes = function() {
    var len = this.readUInt16BE();
    return this.read(len);
};

DataInputX.prototype.readInt8 = function() {
    // var value = this.buf[this.offset];
    var value = this.buf.readInt8(this.offset);
    this.offset += 1;
    return value;
};

DataInputX.prototype.readUInt8 = function() {
    var value = this.buf[this.offset];
    this.offset += 1;
    return value;
};

DataInputX.prototype.readShort = function() {
    var value = this.buf.readInt16BE(this.offset);
    this.offset += 2;
    return value;
};

DataInputX.prototype.readInt16BE = function() {
    var value = this.buf.readInt16BE(this.offset);
    this.offset += 2;
    return value;
};

DataInputX.prototype.readUInt16BE = function() {
    var value = this.buf.readUInt16BE(this.offset);
    this.offset += 2;
    return value;
};

DataInputX.prototype.readInt24BE = function() {
    var ch1 = this.buf[this.offset];
    var ch2 = this.buf[this.offset + 1];
    var ch3 = this.buf[this.offset + 2];
    this.offset += 3;
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8)) >> 8;
};

DataInputX.prototype.readInt = function() {
    var value = this.buf.readInt32BE(this.offset);
    this.offset += 4;
    return value;
};

DataInputX.prototype.readInt32BE = function() {
    var value = this.buf.readInt32BE(this.offset);
    this.offset += 4;
    return value;
};

DataInputX.prototype.readUInt32BE = function() {
    var value = this.buf.readUInt32BE(this.offset);
    this.offset += 4;
    return value;
};

DataInputX.prototype.readInt40BE = function(){
    var value = this.buf.readIntBE(this.offset, 5);
    this.offset += 5;
    return value;
};

DataInputX.prototype.readFloat = function() {
    var value = this.buf.readFloatBE(this.offset);
    this.offset += 4;
    return value;
};

DataInputX.prototype.readValue = function() {
    var type = this.readByte();
    return ValueEnum.create(type).read(this);
};

DataInputX.prototype.readLong = function(){
    var low = this.readInt32BE();
    var high = this.readInt32BE();
    var l = Long.fromBits(high, low);
    return l;
};

DataInputX.prototype.readPack = function () {
    try {
        var type = this.readInt16BE();
        switch (type) {
            case PackEnum.COUNTER_1: return new CounterPack().read(this);
            case PackEnum.ACTIVESTACK_1: return new ActiveStackPack().read(this);
            case PackEnum.TEXT: return new TextPack().read(this);
            case PackEnum.STAT_HTTPC: return new StatHttpcPack().read(this);
            case PackEnum.STAT_ERROR: return new StatErrorPack().read(this);
            case PackEnum.EVENT: return new EventPack().read(this);
            case PackEnum.PARAMETER: return new ParamPack().read(this);
            case PackEnum.HITMAP_1: return new HitMapPack1().read(this);
            case PackEnum.ERROR_SNAP_1: return new ErrorSnapPack1().read(this);
        }
        return null;
    } catch (e) {
        require('../logger').printError('WHATAP-502', "DataInputX (readPack)", e , false);
        return null;
    }
};

DataInputX.prototype.readStep = function() {
    // TODO : empty function need to be implemented
};

DataInputX.prototype.readBoolean = function() {
    var isTrue = this.readByte();
    if (isTrue == 1) {
        return true;
    } else {
        return false;
    }
};

DataInputX.prototype.readFloatArray = function() {
    var length = this.readShort(),
        data = new Array(length);
    for(var i=0; i<length; i++) {
        data[i] = this.readFloat();
    }
    return data;
};

DataInputX.prototype.readIntArray = function() {
    var length = this.readShort(),
        data = new Array(length);
    for (var i = 0; i < length; i++) {
        data[i] = this.readInt();
    }
    return data;
};

DataInputX.prototype.readLongArray = function() {
    var length = this.readShort(),
        data = new Array(length);

    for (var i = 0; i < length; i++) {
        data[i] = this.readLong();
    }
    return data;
};

DataInputX.toBoolean = function(buf, pos) {
    return buf[pos] != 0;
};

DataInputX.toShort = function(buf, pos) {
    var ch1 = buf[pos] & 0xff;
    var ch2 = buf[pos + 1] & 0xff;
    return ((ch1 << 8) + (ch2 << 0));
};

DataInputX.toInt3 = function(buf, pos) {
    var ch1 = buf[pos] & 0xff;
    var ch2 = buf[pos + 1] & 0xff;
    var ch3 = buf[pos + 2] & 0xff;
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8)) >> 8;
};

DataInputX.toInt = function(buf, pos) {
    var ch1 = buf[pos] & 0xff;
    var ch2 = buf[pos + 1] & 0xff;
    var ch3 = buf[pos + 2] & 0xff;
    var ch4 = buf[pos + 3] & 0xff;
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
};

DataInputX.toLong5 = function(buf, pos) {
    return (buf[pos] << 32) +
        ((buf[pos + 1] & 0xff) << 24)+
        ((buf[pos + 2] & 0xff) << 16)+
        ((buf[pos + 3] & 0xff) << 8) +
        ((buf[pos + 4] & 0xff) << 0);
};

DataInputX.get = function(buf, pos, length) {
    var out = Buffer.alloc(length);
    buf.copy(out, pos, length);
    return out;
};

module.exports = DataInputX;