export function stringToByteSeq(buffer) {

    var res = []

    var data = buffer.replace(/[^0-9a-fA-F]/g,"");

    if ((data.length % 2) !== 0) { //add extra zero to output
        data = '0' + data;
    }

    var bytelist = data.match(/.{1,2}/g);
    for (var i in bytelist) {
        var intval = parseInt(bytelist[i],16);
        res.push(intval);
    }
    return res;

}

export function toHex(number, length) {
    'use strict';

    if (typeof number === "undefined") {
        console.log("Assert, null trying to be displayed");
        return "ZZ";
    }


    var s = number.toString(16).toUpperCase();
    while (s.length < length) {
        s = '0' + s;
    }
    return s;
}

export function hexInvert(s) {
    'use strict';
    if ((s.length % 2) !== 0) {
        throw new Error("Non even bytes in string");
    }
    var data = s.replace(/[^0-9a-fA-F]/g,"")
    var bytelist = data.match(/.{1,2}/g);
    var res = "";

    for (var i in bytelist) {
        var val = parseInt(bytelist[i],16);
        val = val ^ 0xFF;

        res += toHex(val,2);
    }
    return res;
}

export function hexEncode(value,len) {
    'use strict';
    var s = "";
    for (var i=0;i<len;i++) {
        s = toHex((value>>(i*8))&0xFF,2) + s;
    }
    return s;

}

export function reverseByteString(s) {
    'use strict';
    if ((s.length % 2) !== 0) {
        throw new Error("Non even bytes in string");
    }
    var res = ""
    for (var i=0;i< s.length;i+=2) {
        res = res + s[s.length-2-i];
        res = res + s[s.length-1-i];
    }
    return res;
}

export function alignToLength(number, length) {
    'use strict';

    var s = number;
    while (s.length < length) {
        s = '0' + s;
    }
    return s;
}

export function toChar(number) {
    'use strict';
    if (number > 127) return '.'
    return number <= 32 ? '.' : String.fromCharCode(number);
}

export function generateUUID() {
    'use strict';
    var d = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    return uuid;
}

/**
 * JavaScript BitArray - v0.2.0
 *
 * Licensed under the revised BSD License.
 * Copyright 2010-2012 Bram Stein
 * All rights reserved.
 */

/**
 * Creates a new empty BitArray with the given length or initialises the BitArray with the given hex representation.
 */
export const BitArray = function (size, hex) {
    this.values = [];

    if (hex) {
        hex = hex.slice(/^0x/.exec(hex) ? 2 : 0);

        if (hex.length * 4 > this.length) {
            throw 'Hex value is too large for this bit array.'
        } else if (hex.length * 4 < this.length) {
            // pad it
            while(hex.length * 4 < this.length) {
                hex = '0' + hex;
            }
        }

        for (var i = 0; i < (hex.length / 8); i++) {
            var slice = hex.slice(i * 8, i * 8 + 8);
            this.values[i] = parseInt(slice, 16);
        }
    } else {
        for (var i = 0; i < Math.ceil(size / 32); i += 1) {
            this.values[i] = 0;
        }
    }
};

/**
 * Returns the total number of bits in this BitArray.
 */
BitArray.prototype.size = function () {
    return this.values.length * 32;
};

/**
 * Sets the bit at index to a value (boolean.)
 */
BitArray.prototype.set = function (index, value) {
    var i = Math.floor(index / 32);
    // Since "undefined | 1 << index" is equivalent to "0 | 1 << index" we do not need to initialise the array explicitly here.
    if (value) {
        this.values[i] |= 1 << index - i * 32;
    } else {
        this.values[i] &= ~(1 << index - i * 32);
    }
    return this;
};

/**
 * Toggles the bit at index. If the bit is on, it is turned off. Likewise, if the bit is off it is turned on.
 */
BitArray.prototype.toggle = function (index) {
    var i = Math.floor(index / 32);
    this.values[i] ^= 1 << index - i * 32;
    return this;
};

/**
 * Returns the value of the bit at index (boolean.)
 */
BitArray.prototype.get = function (index) {
    var i = Math.floor(index / 32);
    return !!(this.values[i] & (1 << index - i * 32));
};

/**
 * Resets the BitArray so that it is empty and can be re-used.
 */
BitArray.prototype.reset = function () {
    this.values = [];
    return this;
};

/**
 * Returns a copy of this BitArray.
 */
BitArray.prototype.copy = function () {
    var cp = new BitArray();
    cp.length = this.length;
    cp.values = [].concat(this.values);
    return cp;
};

/**
 * Returns true if this BitArray equals another. Two BitArrays are considered
 * equal if both have the same length and bit pattern.
 */
BitArray.prototype.equals = function (x) {
    return this.values.length === x.values.length &&
        this.values.every(function (value, index) {
            return value === x.values[index];
        });
};

/**
 * Returns the JSON representation of this BitArray.
 */
BitArray.prototype.toJSON = function () {
    return JSON.stringify(this.values);
};

/**
 * Returns a string representation of the BitArray with bits
 * in logical order.
 */
BitArray.prototype.toString = function () {
    return this.toArray().map(function (value) {
        return value ? '1' : '0';
    }).join('');
};

/**
 * Returns a string representation of the BitArray with bits
 * in mathemetical order.
 */
BitArray.prototype.toBinaryString = function () {
    return this.toArray().map(function (value) {
        return value ? '1' : '0';
    }).reverse().join('');
};

/**
 * Returns a hexadecimal string representation of the BitArray
 * with bits in logical order.
 */
BitArray.prototype.toHexString = function () {
    var result = [];

    for (var i = 0; i < this.values.length; i += 1) {
        result.push(('00000000' + (this.values[i] >>> 0).toString(16)).slice(-8));
    }
    return result.join('');
};

/**
 * Convert the BitArray to an Array of boolean values.
 */
BitArray.prototype.toArray = function () {
    var result = [];

    for (var i = 0; i < this.values.length * 32; i += 1) {
        result.push(this.get(i));
    }
    return result;
};

/**
 * Returns the total number of bits set to one in this BitArray.
 */
BitArray.prototype.count = function () {
    var total = 0;

    // If we remove the toggle method we could efficiently cache the number of bits without calculating it on the fly.
    this.values.forEach(function (x) {
        // See: http://bits.stephan-brumme.com/countBits.html for an explanation
        x  = x - ((x >> 1) & 0x55555555);
        x  = (x & 0x33333333) + ((x >> 2) & 0x33333333);
        x  = x + (x >> 4);
        x &= 0xF0F0F0F;

        total += (x * 0x01010101) >> 24;
    });
    return total;
};

/**
 * Inverts this BitArray.
 */
BitArray.prototype.not = function () {
    this.values = this.values.map(function (v) {
        return ~v;
    });
    return this;
};

/**
 * Bitwise OR on the values of this BitArray using BitArray x.
 */
BitArray.prototype.or = function (x) {
    if (this.values.length !== x.values.length) {
        throw 'Arguments must be of the same length.';
    }
    this.values = this.values.map(function (v, i) {
        return v | x.values[i];
    });
    return this;
};

/**
 * Bitwise AND on the values of this BitArray using BitArray x.
 */
BitArray.prototype.and = function (x) {
    if (this.values.length !== x.values.length) {
        throw 'Arguments must be of the same length.';
    }
    this.values = this.values.map(function (v, i) {
        return v & x.values[i];
    });
    return this;
};

/**
 * Bitwise XOR on the values of this BitArray using BitArray x.
 */
BitArray.prototype.xor = function (x) {
    if (this.values.length !== x.values.length) {
        throw 'Arguments must be of the same length.';
    }
    this.values = this.values.map(function (v, i) {
        return v ^ x.values[i];
    });
    return this;
};
