define([
    'jquery',
    '/common/common-util.js',
    '/common/visible.js',
    '/common/common-hash.js',
    '/file/file-crypto.js',
    '/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, Util, Visible, Hash, FileCrypto) {
    var Nacl = window.nacl;
    var Thumb = {
        dimension: 100,
        padDimension: 200,
        UPDATE_INTERVAL: 60000,
        UPDATE_FIRST: 5000
    };

    var supportedTypes = [
        'text/plain',
        'image/png',
        'image/jpeg',
        'image/jpg',
        'image/gif',
        'video/',
        'application/pdf'
    ];

    Thumb.isSupportedType = function (file) {
        if (!file) { return false; }
        var type = file.type;
        if (Util.isPlainTextFile(file.type, file.name)) {
            type = "text/plain";
        }
        return supportedTypes.some(function (t) {
            return type.indexOf(t) !== -1;
        });
    };

    // create thumbnail image from metadata
    // return an img tag, or undefined if anything goes wrong
    Thumb.fromMetadata = function (metadata) {
        if (!metadata || typeof(metadata) !== 'object' || !metadata.thumbnail) { return; }
        try {
            var u8 = Nacl.util.decodeBase64(metadata.thumbnail);
            var blob = new Blob([u8], {
                type: 'image/png'
            });
            var url = URL.createObjectURL(blob);
            var img = new Image();
            img.src = url;
            img.width = Thumb.dimension;
            img.height = Thumb.dimension;
            return img;
        } catch (e) {
            console.error(e);
            return;
        }
    };

    var getResizedDimensions = Thumb.getResizedDimensions = function (img, type) {
        var h = type === 'video' ? img.videoHeight : img.height;
        var w = type === 'video' ? img.videoWidth : img.width;

        var dim = type === 'pad' ? Thumb.padDimension : Thumb.dimension;

        // if the image is too small, don't bother making a thumbnail
        /*if (h <= dim && w <= dim) {
            return {
                x: Math.floor((dim - w) / 2),
                w: w,
                y: Math.floor((dim - h) / 2),
                h : h
            };
        }*/

        // the image is taller than it is wide, so scale to that.
        var r = dim / (h > w? h: w); // ratio
        if (h <= dim && w <= dim) { r = 1; }

        var d;
        if (h > w) {
            var newW = Math.floor(w*r);
            d = Math.floor((dim - newW) / 2);
            return {
                dim: dim,
                x: d,
                w: newW,
                y: 0,
                h: dim,
            };
        } else {
            var newH = Math.floor(h*r);
            d = Math.floor((dim - newH) / 2);
            return {
                dim: dim,
                x: 0,
                w: dim,
                y: d,
                h: newH
            };
        }
    };

    // assumes that your canvas is square
    // nodeback returning blob
    Thumb.fromCanvas = function (canvas, D, cb) {
        var c2 = document.createElement('canvas');
        if (!D) { return void cb('ERROR'); }

        c2.width = D.dim;
        c2.height = D.dim;

        var ctx = c2.getContext('2d');
        try {
            ctx.drawImage(canvas, D.x, D.y, D.w, D.h);
        } catch (e) {
            return void cb('ERROR');
        }
        cb(void 0, c2.toDataURL());
    };

    Thumb.fromImageBlob = function (blob, cb) {
        var url = URL.createObjectURL(blob);
        var img = new Image();

        img.onload = function () {
            var D = getResizedDimensions(img, 'image');
            Thumb.fromCanvas(img, D, cb);
        };
        img.onerror = function () {
            cb('ERROR');
        };
        img.src = url;
    };
    Thumb.fromVideoBlob = function (blob, cb) {
        var url = URL.createObjectURL(blob);
        var video = document.createElement("VIDEO");
        video.addEventListener('loadeddata', function() {
            var D = getResizedDimensions(video, 'video');
            Thumb.fromCanvas(video, D, cb);
        }, false);
        video.addEventListener('error', function (e) {
            console.error(e);
            cb('ERROR');
        });
        video.src = url;
    };
    Thumb.fromPdfBlob = function (blob, cb) {
        require.config({paths: {'pdfjs-dist': '/common/pdfjs'}});
        require(['pdfjs-dist/build/pdf'], function (PDFJS) {
            var url = URL.createObjectURL(blob);
            var makeThumb = function (page) {
                var vp = page.getViewport(1);
                var canvas = document.createElement("canvas");
                canvas.width = canvas.height = Thumb.dimension;
                var scale = Math.min(canvas.width / vp.width, canvas.height / vp.height);
                canvas.width = Math.floor(vp.width * scale);
                canvas.height = Math.floor(vp.height * scale);
                return page.render({
                    canvasContext: canvas.getContext("2d"),
                    viewport: page.getViewport(scale)
                }).promise.then(function () {
                    return canvas;
                }).catch(function () {
                    cb('ERROR');
                });
            };
            PDFJS.getDocument(url).promise
            .then(function (doc) {
                return doc.getPage(1).then(makeThumb).then(function (canvas) {
                    var D = getResizedDimensions(canvas, 'pdf');
                    Thumb.fromCanvas(canvas, D, cb);
                });
            }).catch(function () {
                cb('ERROR');
            });
        });
    };
    Thumb.fromPlainTextBlob = function (blob, cb) {
        var canvas = document.createElement("canvas");
        canvas.width = canvas.height = Thumb.dimension;
        var reader = new FileReader();
        reader.addEventListener('loadend', function (e) {
            var content = e.srcElement.result;
            var lines = content.split("\n");
            var canvasContext = canvas.getContext("2d");
            var fontSize = 4;
            canvas.height = (lines.length) * (fontSize + 1);
            canvasContext.font = fontSize + 'px monospace';
            lines.forEach(function (text, i) {

                canvasContext.fillText(text, 5, i * (fontSize + 1));
            });
            var D = getResizedDimensions(canvas, "txt");
            Thumb.fromCanvas(canvas, D, cb);
        });
        reader.readAsText(blob);
    };
    Thumb.fromBlob = function (blob, _cb) {
        var cb = Util.once(_cb);
        // The blob is already in memory, it should be super-fast to make a thumbnail
        // ==> 1s timeout
        setTimeout(function () {
            cb('TIMEOUT');
        }, 1000);
        try {
            if (blob.type.indexOf('video/') !== -1) {
                return void Thumb.fromVideoBlob(blob, cb);
            }
            if (blob.type.indexOf('application/pdf') !== -1) {
                return void Thumb.fromPdfBlob(blob, cb);
            }
            if (Util.isPlainTextFile(blob.type, blob.name)) {
                return void Thumb.fromPlainTextBlob(blob, cb);
            }
            if (blob.type.indexOf('image/') !== -1) {
                return void Thumb.fromImageBlob(blob, cb);
            }
        } catch (e) {
            return void cb('THUMBNAIL_ERROR');
        }
        return void cb('NO_THUMBNAIL');
    };

    window.html2canvas = undefined;
    Thumb.fromDOM = function (opts, cb) {
        var element = opts.getContainer();
        if (!element) { return; }
        var todo = function () {
            if (opts.filter) { opts.filter(element, true); }
            window.html2canvas(element, {
                allowTaint: true,
                onrendered: function (canvas) {
                    if (opts.filter) { opts.filter(element, false); }
                    setTimeout(function () {
                        var D = getResizedDimensions(canvas, 'pad');
                        Thumb.fromCanvas(canvas, D, cb);
                    }, 10);
                }
            });
        };
        if (window.html2canvas) { return void todo(); }
        require(['/bower_components/html2canvas/build/html2canvas.min.js'], todo);
    };

    Thumb.initPadThumbnails = function (common, opts) {
        if (!opts.type || !opts.getContent) {
            throw new Error("type and getContent are needed for thumbnails");
        }
        var oldThumbnailState;
        var mkThumbnail = function () {
            var content = opts.getContent();
            if (content === oldThumbnailState) { return; }
            oldThumbnailState = content;
            Thumb.fromDOM(opts, function (err, b64) {
                Thumb.setPadThumbnail(common, opts.type, null, b64);
            });
        };
        var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
        var to;
        var tUntil;
        var interval = function () {
            tUntil = nafa();
            if (tUntil) {
                window.clearTimeout(to);
                to = window.setTimeout(interval, tUntil+1);
                return;
            }
            to = window.setTimeout(interval, Thumb.UPDATE_INTERVAL+1);
        };
        Visible.onChange(function (v) {
            if (v) {
                window.clearTimeout(to);
                return;
            }
            interval();
        });
        if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); }
    };


    var addThumbnail = function (err, thumb, $span, cb) {
        var split = thumb.split(',');
        var u8 = Nacl.util.decodeBase64(split[1] || split[0]);
        var blob = new Blob([u8], {
            type: 'image/png'
        });
        var url = URL.createObjectURL(blob);
        var img = new Image();
        img.src = url;
        $span.find('.cp-icon').hide();
        $span.prepend(img);
        cb($(img));
    };
    Thumb.addThumbnail = function(thumb, $span, cb) {
        return addThumbnail(null, thumb, $span, cb);
    };
    var getKey = function (type, channel) {
        return 'thumbnail-' + type + '-' + channel;
    };
    Thumb.setPadThumbnail = function (common, type, channel, b64, cb) {
        cb = cb || function () {};
        channel = channel || common.getMetadataMgr().getPrivateData().channel;
        var k = getKey(type, channel);
        common.setThumbnail(k, b64, cb);
    };
    Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
        cb = cb || function () {};
        var parsed = Hash.parsePadUrl(href);
        var k = getKey(parsed.type, channel);
        var whenNewThumb = function () {
            var privateData = common.getMetadataMgr().getPrivateData();
            var fileHost = privateData.fileHost || privateData.origin;
            var secret = Hash.getSecrets('file', parsed.hash, password);
            var hexFileName = secret.channel;
            var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
            var key = secret.keys && secret.keys.cryptKey;
            FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
                if (e) {
                    if (e === 'XHR_ERROR') { return; }
                    return console.error(e);
                }
                if (!metadata) { return console.error("NO_METADATA"); }

                var v = metadata.thumbnail;
                if (!v) {
                    v = 'EMPTY';
                }
                Thumb.setPadThumbnail(common, parsed.type, hexFileName, v, function (err) {
                    if (!metadata.thumbnail) { return; }
                    addThumbnail(err, metadata.thumbnail, $container, cb);
                });
            });
        };
        common.getThumbnail(k, function (err, v) {
            if (!v && parsed.type === 'file') {
                // We can only create thumbnails for files here since we can't easily decrypt pads
                return void whenNewThumb();
            }
            if (!v) { return; }
            if (v === 'EMPTY') { return; }
            addThumbnail(err, v, $container, cb);
        });
    };

    return Thumb;
});