define([
    '/file/file-crypto.js',
    '/common/common-hash.js',
    '/bower_components/tweetnacl/nacl-fast.min.js',
], function (FileCrypto, Hash) {
    var Nacl = window.nacl;
    var module = {};

    module.upload = function (file, noStore, common, updateProgress, onComplete, onError, onPending) {
        var u8 = file.blob; // This is not a blob but a uint8array
        var metadata = file.metadata;

        // if it exists, path contains the new pad location in the drive
        var path = file.path;

        var key = Nacl.randomBytes(32);
        var next = FileCrypto.encrypt(u8, metadata, key);

        var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);

        var sendChunk = function (box, cb) {
            var enc = Nacl.util.encodeBase64(box);
            common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
                cb(e, msg);
            });
        };

        var actual = 0;
        var again = function (err, box) {
            if (err) { throw new Error(err); }
            if (box) {
                actual += box.length;
                var progressValue = (actual / estimate * 100);
                updateProgress(progressValue);

                return void sendChunk(box, function (e) {
                    if (e) { return console.error(e); }
                    next(again);
                });
            }

            if (actual !== estimate) {
                console.error('Estimated size does not match actual size');
            }

            // if not box then done
            common.uploadComplete(function (e, id) {
                if (e) { return void console.error(e); }
                var uri = ['', 'blob', id.slice(0,2), id].join('/');
                console.log("encrypted blob is now available as %s", uri);

                var b64Key = Nacl.util.encodeBase64(key);

                var hash = Hash.getFileHashFromKeys(id, b64Key);
                var href = '/file/#' + hash;

                var title = metadata.name;

                if (noStore) { return void onComplete(href); }

                common.initialPath = path;
                common.renamePad(title || "", href, function (err) {
                    if (err) { return void console.error(err); }
                    onComplete(href);
                    common.setPadAttribute('fileType', metadata.type, null, href);
                });
            });
        };

        common.uploadStatus(estimate, function (e, pending) {
            if (e) {
                console.error(e);
                onError(e);
                return;
            }

            if (pending) {
                return void onPending(function () {
                    // if the user wants to cancel the pending upload to execute that one
                    common.uploadCancel(function (e, res) {
                        if (e) {
                            return void console.error(e);
                        }
                        console.log(res);
                        next(again);
                    });
                });
            }
            next(again);
        });
    };
    return module;
});