define([
    '/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
    var Nacl = window.nacl;
    var Curve = {};

    var concatenateUint8s = function (A) {
        var len = 0;
        var offset = 0;
        A.forEach(function (uints) {
            len += uints.length || 0;
        });
        var c = new Uint8Array(len);
        A.forEach(function (x) {
            c.set(x, offset);
            offset += x.length;
        });
        return c;
    };

    var encodeBase64 = Nacl.util.encodeBase64;
    var decodeBase64 = Nacl.util.decodeBase64;
    var decodeUTF8 = Nacl.util.decodeUTF8;
    var encodeUTF8 = Nacl.util.encodeUTF8;

    Curve.encrypt = function (message, secret) {
        var buffer = decodeUTF8(message);
        var nonce = Nacl.randomBytes(24);
        var box = Nacl.box.after(buffer, nonce, secret);
        return encodeBase64(nonce) + '|' + encodeBase64(box);
    };

    Curve.decrypt = function (packed, secret) {
        var unpacked = packed.split('|');
        var nonce = decodeBase64(unpacked[0]);
        var box = decodeBase64(unpacked[1]);
        var message = Nacl.box.open.after(box, nonce, secret);
        if (message === false) { return null; }
        return encodeUTF8(message);
    };

    Curve.signAndEncrypt = function (msg, cryptKey, signKey) {
        var packed = Curve.encrypt(msg, cryptKey);
        return encodeBase64(Nacl.sign(decodeUTF8(packed), signKey));
    };

    Curve.openSigned = function (msg, cryptKey /*, validateKey STUBBED*/) {
        var content = decodeBase64(msg).subarray(64);
        return Curve.decrypt(encodeUTF8(content), cryptKey);
    };

    Curve.deriveKeys = function (theirs, mine) {
        try {
            var pub = decodeBase64(theirs);
            var secret = decodeBase64(mine);

            var sharedSecret = Nacl.box.before(pub, secret);
            var salt = decodeUTF8('CryptPad.signingKeyGenerationSalt');

            // 64 uint8s
            var hash = Nacl.hash(concatenateUint8s([salt, sharedSecret]));
            var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
            var cryptKey = hash.subarray(32, 64);

            return {
                cryptKey: encodeBase64(cryptKey),
                signKey: encodeBase64(signKp.secretKey),
                validateKey: encodeBase64(signKp.publicKey)
            };
        } catch (e) {
            console.error('invalid keys or other problem deriving keys');
            console.error(e);
            return null;
        }
    };

    Curve.createEncryptor = function (keys) {
        if (!keys || typeof(keys) !== 'object') {
            return void console.error("invalid input for createEncryptor");
        }

        var cryptKey = decodeBase64(keys.cryptKey);
        var signKey = decodeBase64(keys.signKey);
        var validateKey = decodeBase64(keys.validateKey);

        return {
            encrypt: function (msg) {
                return Curve.signAndEncrypt(msg, cryptKey, signKey);
            },
            decrypt: function (packed) {
                return Curve.openSigned(packed, cryptKey, validateKey);
            }
        };
    };

    return Curve;
});