import kdbxweb from 'kdbxweb';

const SecureInput = function() {
    this.el = null;
    this.minChar = 0x1400 + Math.round(Math.random() * 100);
    this.maxLen = 1024;
    this.length = 0;
    this.pseudoValue = '';
    this.salt = new Uint32Array(0);
};

SecureInput.prototype.setElement = function(el) {
    this.el = el;
    this.el.val(this.pseudoValue);
    this.el.on('input', this._input.bind(this));
};

SecureInput.prototype.reset = function() {
    this.el = null;
    this.length = 0;
    this.pseudoValue = '';

    if (this.salt) {
        for (let i = 0; i < this.salt.length; i++) {
            this.salt[i] = 0;
        }
    }
    this.salt = new Uint32Array(0);
};

SecureInput.prototype._input = function() {
    const selStart = this.el[0].selectionStart;
    const value = this.el.val();
    let newPs = '';
    const newSalt = new Uint32Array(this.maxLen);
    let valIx = 0,
        psIx = 0;
    while (valIx < value.length) {
        const valCh = value.charCodeAt(valIx);
        const psCh = this.pseudoValue.charCodeAt(psIx);
        const isSpecial = this._isSpecialChar(valCh);
        if (psCh === valCh) {
            // not changed
            newPs += this._getChar(newPs.length);
            newSalt[newPs.length - 1] = psCh ^ this.salt[psIx] ^ newPs.charCodeAt(newPs.length - 1);
            psIx++;
            valIx++;
        } else if (isSpecial) {
            // deleted
            psIx++;
        } else {
            // inserted or replaced
            newPs += this._getChar(newPs.length);
            newSalt[newPs.length - 1] = newPs.charCodeAt(newPs.length - 1) ^ valCh;
            valIx++;
        }
    }
    this.length = newPs.length;
    this.pseudoValue = newPs;
    this.salt = newSalt;
    this.el.val(newPs);
    this.el[0].selectionStart = selStart;
    this.el[0].selectionEnd = selStart;
};

SecureInput.prototype._getChar = function(ix) {
    return String.fromCharCode(this.minChar + ix);
};

SecureInput.prototype._isSpecialChar = function(ch) {
    return ch >= this.minChar && ch <= this.minChar + this.maxLen;
};

Object.defineProperty(SecureInput.prototype, 'value', {
    enumerable: true,
    get() {
        const pseudoValue = this.pseudoValue;
        const salt = this.salt;
        const len = pseudoValue.length;
        let byteLength = 0;
        const valueBytes = new Uint8Array(len * 4);
        const saltBytes = kdbxweb.Random.getBytes(len * 4);
        let ch;
        let bytes;
        for (let i = 0; i < len; i++) {
            ch = String.fromCharCode(pseudoValue.charCodeAt(i) ^ salt[i]);
            bytes = kdbxweb.ByteUtils.stringToBytes(ch);
            for (let j = 0; j < bytes.length; j++) {
                valueBytes[byteLength] = bytes[j] ^ saltBytes[byteLength];
                byteLength++;
            }
        }
        return new kdbxweb.ProtectedValue(
            valueBytes.buffer.slice(0, byteLength),
            saltBytes.buffer.slice(0, byteLength)
        );
    }
});

export { SecureInput };
