/*
goog.require('jQuery');
goog.require('divvy.core');
goog.require('divvy.client');
goog.require('swfupload');
goog.require('swfupload.speed');
goog.require('swfupload.cookies');
goog.require('swfupload.queue');
goog.provide('divvy.upload');
*/
divvy.upload = divvy.upload || {};

divvy.upload.NativeUploader = Class.extend(
    {
        init: function(config){
            this.queue = [];
            this.url = config.upload_url;
            this.onFileQueued = config.file_queued_handler;
            this.onUploadStart = config.upload_start_handler;
            this.onUploadProgress = config.upload_progress_handler;
            this.onUploadComplete = config.upload_complete_handler;
            this.onUploadSuccess = config.upload_success_handler;
            this.count = 0;
            this.working = false;
            this.max_simultaneous_uploads = config.max_simultaneous_uploads || 1;
        },
        addFile: function(native_file){
            this.count += 1;
            var file = {
                native_file: native_file,
                size: native_file.fileSize,
                name: native_file.fileName,
                id: this.count
            };
            this.queue.push(file);
            this.onFileQueued(file);
        },

        getStats: function(){
            return {
                files_queued: this.queue.length + (this.working ? 1:0)
            };
        },

        upload: function(file){
            this.onUploadStart(file);

            var boundary = '------multipartformboundary' + (new Date).getTime();
            var dashdash = '--';
            var crlf     = '\r\n';

            /* Build RFC2388 string. */
            var builder = '';

            builder += dashdash;
            builder += boundary;
            builder += crlf;


            var native_file = file.native_file;
            var xhr = new XMLHttpRequest();
            var self = this;

            builder += 'Content-Disposition: form-data; name="image"';
            if (native_file.fileName){
                builder += '; filename="' + native_file.fileName + '"';
            }
            builder += crlf;

            builder += 'Content-Type: application/octet-stream';
            builder += crlf;
            builder += crlf;

            /* Append binary data. */
            builder += native_file.getAsBinary();
            builder += crlf;

            /* Write boundary. */
            builder += dashdash;
            builder += boundary;
            builder += crlf;

            /* Mark end of the request. */
            builder += dashdash;
            builder += boundary;
            builder += dashdash;
            builder += crlf;

            xhr.upload.addEventListener(
                "progress",
                function(e){
                    var bytes = 0;
                    if (e.lengthComputable){
                        bytes = e.loaded;
                        var percentage;
                        if (e.loaded > e.total){
                            percentage = 100;
                        } else {
                            percentage = Math.round((e.loaded*100)/e.total);
                        }
                    }
                    self.onUploadProgress(file, bytes, e.total);
                }, false);
            xhr.upload.addEventListener(
                "load",
                function(e){
                    //self.onUploadSuccess(file, 'response text here?');
                }, false);
            xhr.onreadystatechange = function(){
                if (xhr.readyState == 4){
                    self.working--;
                    self.onUploadSuccess(file, xhr.responseText);
                }
            };
            xhr.open("POST", this.url);

            xhr.setRequestHeader('content-type', 'multipart/form-data; boundary='
                                 + boundary);
            xhr.sendAsBinary(builder);
            delete builder;
        },
        startUpload: function(){
            while (this.working < this.max_simultaneous_uploads && this.queue.length > 0){
                this.working++;
                this.upload(this.queue.shift());
            }
        }
    });

divvy.upload.Uploader = Class.extend(
    {
        defaultConfig:{
            queue_wrapper_selector: "#upload-queue",
            templates_selector: "#upload-queue-templates",
            position_over: "#add-your-photo",
            capture_clicks: true,
            hide_individual_files: true,
            event_slug: null, // this must be passed in
            onFileDialogStart: null,
            checkPhotoProcessed: null,
            checkPhotosProcessed: function(photo){
                return true;
            },
            swfupload_config: {
                upload_url: null,
                flash_url: null,
                file_size_limit: "30 MB",
                file_post_name : "image",
                file_types : "*.jpg;*.png;*.tiff;*.bmp;*.jpeg;*.gif",
                file_upload_limit : 0,
                file_queue_limit : 0,

                button_placeholder_id: "add-your-photo-swfupload",
                button_text: '',
                button_width: null,
                button_height: null,
                button_text_style: "",
                button_text_left_padding: 0,
                button_text_top_padding: 0,
                button_cursor : SWFUpload.CURSOR.HAND,
                button_window_mode : SWFUpload.WINDOW_MODE.TRANSPARENT
            }
        },

        nativeFileUploadSupported: function(){
            try{
                new FileReader();
                return true;
            } catch (e){
                return false;
            }
        },

        swfInit: function(){
            this.swfUpload = new SWFUpload(this.config.swfupload_config);
        },

        nativeInit: function(){
            var overlay = $("<div id='uploader_overlay'><p>Drop image files here to upload them.</p></div>")
                .appendTo("body");
            var url = divvy.client.newSimpleClient().getEventPhotosUrl(this.config.event_slug);
            this.nativeUploader = new divvy.upload.NativeUploader(this.config.swfupload_config);
            this.swfUpload = this.nativeUploader;
            if ($("#multifile").length == 0){
                $(this.config.position_over).append(
                    '<input id="multifile" type="file" multiple="" accept="image/*"/>');
            }
            var self = this;
            $("#multifile").click(divvy.caller(this.handleFileDialogStart, this));
            $("#multifile").change(
                function(){
                    var files = this.files;
                    for (var i=0,ii=files.length; i < ii; i++){
                        var file = files[i];
                        self.nativeUploader.addFile(file);
                    }
                    self.handleFileDialogComplete(files.length,
                                                  files.length,
                                                  self.nativeUploader.getStats().files_queued);
                    //self.nativeUploader.startUpload();
                });
            var dropzone = $("body"); // maybe we want to make this a smaller region?
            var overlayTimeout = null;
            dropzone.bind(
                "dragenter",
                function(e){
                    if (!e.originalEvent.dataTransfer.types.contains("Files")){
                        return true;
                    }
                    overlay.show();
                    return false;
                }).bind(
                "dragover",
                function(e){
                    if (!e.originalEvent.dataTransfer.types.contains("Files")){
                        return true;
                    }
                    window.clearTimeout(overlayTimeout);
                    overlayTimeout = window.setTimeout(
                        function(){
                            overlay.hide();
                            self.handleFileDialogComplete(0,0,
                                                  self.nativeUploader.getStats().files_queued);
                        }, 100);
                    return false;
                }).bind(
// XXX: blargh! For some reason, dragleave interferes with the drop event, causing it not to happen.
// This is why we have to screw around with the setTimeout business... boo.
//                "dragleave",
//                function(e){
//                    console.log("Got drag leave", e.originalEvent);
//                    //e.stopPropagation();
//                    //e.preventDefault();
//                    overlay.hide();
//                    return true;
//                }).bind(
                "drop",
                function(e){
                    window.clearTimeout(overlayTimeout);
                    self.handleFileDialogStart();
                    overlay.hide();
                    var files = e.originalEvent.dataTransfer.files;
                    for (var i=0, ii=files.length; i < ii; i++){
                        self.nativeUploader.addFile(files[i]);
                    }
                    self.handleFileDialogComplete(files.length,
                                                  files.length,
                                                  self.nativeUploader.getStats().files_queued);
                    return false;
                });
        },

        init: function(config){
            config = jQuery.extend(true, {}, this.defaultConfig, config||{});
            this.config = config;
            this.swfUpload = null;
            if (this.config.position_over){
                if (!this.config.swfupload_config.button_width){
                    this.config.swfupload_config.button_width = $(this.config.position_over).outerWidth();
                }
                if (!this.config.swfupload_config.button_height){
                    this.config.swfupload_config.button_height = $(this.config.position_over).outerHeight();
                }
            }
            if (!this.config.swfupload_config.upload_url){
                this.config.swfupload_config.upload_url = divvy.client.newSimpleClient()
                    .getEventPhotosUrl(this.config.event_slug);
            }
            if (!this.config.swfupload_config.flash_url){
                this.config.swfupload_config.flash_url = window.location.protocol+"//"+window.location.host+"/static/swf/swfupload.swf";
            }
            // create delegate to this object
            this.config.swfupload_config.mouse_in_handler = divvy.caller(this.handleMouseIn, this);
            this.config.swfupload_config.mouse_out_handler = divvy.caller(this.handleMouseOut, this);

            this.config.swfupload_config.swfupload_loaded_handler = divvy.caller(this.handleSwfuploadLoaded, this);
            this.config.swfupload_config.file_dialog_start_handler = divvy.caller(this.handleFileDialogStart, this);
            this.config.swfupload_config.file_queued_handler = divvy.caller(this.handleFileQueued, this);
            this.config.swfupload_config.file_queue_error_handler = divvy.caller(this.handleFileQueueError, this);
            this.config.swfupload_config.file_dialog_complete_handler = divvy.caller(this.handleFileDialogComplete, this);
            this.config.swfupload_config.upload_start_handler = divvy.caller(this.handleUploadStart, this);
            this.config.swfupload_config.upload_progress_handler = divvy.caller(this.handleUploadProgress, this);
            this.config.swfupload_config.upload_error_handler = divvy.caller(this.handleUploadError, this);
            this.config.swfupload_config.upload_success_handler = divvy.caller(this.handleUploadSuccess, this);
            this.config.swfupload_config.upload_complete_handler = divvy.caller(this.handleUploadComplete, this);

            if (this.nativeFileUploadSupported()){
                this.nativeInit();
            } else {
                this.swfInit();
            }
            var self = this;


            this.container = null;
            this.photo_list = null;
            this.position_over = null;
            this.capture_clicks = config.capture_clicks || false;
            this.hide_individual_files = config.hide_individual_files || false;
            this.templates = $(config.templates_selector);
            if (config.queue_wrapper_selector){
                this.container = $(config.queue_wrapper_selector);
                this.container.addClass('file-upload');
            }
            if (config.photo_list_selector){
                this.photo_list = $(config.photo_list_selector);
            }
            if (config.position_over){
                this.position_over = $(config.position_over);
            }
            this.totalUploadCount = 0;
            this.totalUploadedCount = 0;
            this.totalBytes = 0;
            this.totalUploadedBytes = 0;
            this.startTime = null;
            this.uploadedPhotos = [];
        },

        getFileEl: function(file){
            return $("#"+file.id);
        },

        _chromeMouseOutTimeout: null,
        handleMouseIn: function(){
            this.position_over.find("a").addClass("active");
            if ($.browser.safari && navigator.userAgent.toLowerCase().indexOf('chrome') > -1){
                //Chrome does not recieve mouse out events, so perform the mouse out after a while.
                var self = this;
                this._chromeMouseOutTimeout = window.setTimeout(function(){
                                                                    self.handleMouseOut();
                                                                }, 3000);
            }
        },

        handleMouseOut: function(){
            if (this._chromeMouseOutTimeout){
                window.clearTimeout(this._chromeMouseOutTimeout);
                this._chromeMouseOutTimeout = null;
            }
            this.position_over.find("a").removeClass("active");
        },

        handleSwfuploadLoaded: function(){
            if (this.position_over){
                var position = this.position_over.position();
                $(this.swfUpload.movieElement)
                    .css("position","absolute")
                    .css("top",""+position.top+"px")
                    .css("left",""+position.left+"px")
                    .show();
            }
            if (this.container){
                //            this.container.html("Files to upload will appear here.");
            }
        },
        handleFileDialogStart: function(){
            divvy.console.log("Got to: file_dialog_start_handler:", arguments);
            if (this.container){
                if (this.container.find(".progress").length === 0){
                    this.templates.find(".progress").clone().appendTo(this.container);
                }
                this.updateContainer();
            }
            if (this.config.onFileDialogStart){
                this.config.onFileDialogStart();
            }
        },
        updateContainer: function(){
            var self = this;
            if (!this.container){
                return;
            }
            this.container
                .find(".file-count").html(this.totalUploadCount).end()
                .find(".uploaded-count").html(this.totalUploadedCount).end()
                .find(".total-size").html(this.round(this.totalBytes/1024/1024,2)).end()
                .find(".total-uploaded-size").html(this.round(this.totalUploadedBytes/1024/1024,2)).end()
                .find(".eta").html(this.formatTime(this.getEta())).end()
                .find(".completion-bar").width(Math.max(5, this.getTotalUploadedPercent())+"%")
                .find(".speed").html(this.getSpeed()).end()
                .find(".upload-cancel-button").unbind("click").click(function(){ self.cancelUploads(); }).end();
        },
        getSpeed: function(){
            var elapsed = this.getElapsed()/1000;
            var kbs = this.totalUploadedBytes/1024/elapsed;
            return ""+Math.round(kbs,2)+" kb/s";
        },
        cancelUploads: function(){
            divvy.console.log("Canceling uploads");
            this.swfUpload.cancelQueue();
            this.totalUploadCount = 0;
            this.totalUploadedCount = 0;
            this.totalBytes = 0;
            this.totalUploadedBytes = 0;
            this.startTime = null;
            this.updateContainer();
            this.container.find(".progress").remove();
        },
        handleFileDialogComplete: function(selectedCount, queuedCount, totalInQueue){
            if (totalInQueue === 0){
                this.container.find(".progress").remove();
            }
            divvy.console.log("Got to: file_dialog_complete_handler:", arguments);
        },
        round: function(num, dec){
            return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
        },
        getTotalUploadedPercent: function(partial_bytes){
            if (!partial_bytes){
                partial_bytes = 0;
            }
            return (this.totalUploadedBytes+partial_bytes)/this.totalBytes*100;
        },
        getElapsed: function(){
            if (!this.startTime){
                return 0;
            }
            var now = new Date();
            return now.getTime() - this.startTime.getTime();
        },
        getEta: function(){
            var now = new Date();
            var elapsed = this.getElapsed();
            if (!elapsed){
                return null;
            }

            var estimated_total = elapsed/(this.getTotalUploadedPercent()/100);
            return estimated_total-elapsed;
        },
        formatTime: function(millis){
            if (millis === null || millis === Infinity || millis === NaN){
                return "";
            }
            function pad(n){ return n<=9 ? "0"+n : ""+n; }
            var total_seconds = millis/1000;
            var total_minutes = total_seconds/60;
            var total_hours = total_minutes/60;
            if (total_hours < 1){
                if (total_minutes < 1){
                    return Math.floor(total_seconds)+"s";
                } else {
                    return Math.floor(total_minutes)+":"+pad(Math.floor(total_seconds%60))+"s";
                }
            } else {
                return Math.floor(total_hours)+":"+pad(Math.floor(total_minutes%60))+"m";
            }
        },
        handleFileQueued: function(file){
            this.totalUploadCount += 1;
            this.totalBytes += file.size;
            if (this.container){
                this.updateContainer();
                var el = this.templates.find(".upload-queue-file").clone()
                    .attr("id", file.id)
                    .find(".upload-queue-file-name").html(file.name).end()
                    .find(".upload-queue-file-size").html(""+Math.round(file.size/1024)+"kb").end();
                if (this.hide_individual_files){
                    el.hide();
                }
                el.appendTo(this.container);
            }
            if (this.photo_list){
                this.photo_list.find("tr").each(
                    function(){
                        var lastThumb = $(this).find("td:eq(4)");
                        if (lastThumb.length > 0){
                            if ($(this).next().length === 0){
                                $(this).after("<tr></tr>");
                            }
                            lastThumb.prependTo($(this).next());
                        }
                    });
                if (this.photo_list.find("tr").length === 0){
                    this.photo_list.append("<tr></tr>");
                }
                this.photo_list
                    .find("tr:eq(0)")
                    .prepend('  <td class="photo-tile" id="upload-photo-'+file.id+'">'
                             + '  <div class="thumbnail-box">'
                             + '    <a href="">'
                             + '    </a>'
                             + '  </div>'
                             + '  <div class="title">'
                             + '    <p> by <a href="">...</a></p>'
                             + '  </div>'
                             + '</td>');
            }
            try {
                this.swfUpload.startUpload();
            } catch (e){
                divvy.console.log("Failed to call startUpload.  Error was:", e);
                // this occasionally fails for no apparent reason.
                divvy.alert("There was a problem starting your upload.  Please refresh the page and try again.");
            }

        },
        handleFileQueueError: function(file, error, message){
            var msg = "The file "+file.name+" ";
            var E = SWFUpload.QUEUE_ERROR;
            switch (error){
            case E.FILE_EXCEEDS_SIZE_LIMIT:
                msg += "exceeds the size limit of "+this.swfUpload.settings.file_size_limit;
                break;
            case E.ZERO_BYTE_FILE:
                msg += "does not contain any data";
                break;
            case E.INVALID_FILETYPE:
                msg += "is not a supported filetype";
                break;
            case E.QUEUE_LIMIT_EXCEEDED:
                msg = "You can only upload "+this.swfUpload.settings.file_queue_limit+" files at a time";
                break;
            default:
                msg = "An unknown error occured: "+message;
                break;
            }
            divvy.alert(msg);
        },
        getUploadPhotoEl: function(file){
            return $(document.getElementById("upload-photo-"+file.id));
        },
        retarget_links: function(){
            var href = $(this).attr("href");
            if (href && href.substr(1) !== "#"){
                $(this).attr("target", "_blank")
                    .addClass("divvy-upload-retargeted");
            }
        },
        handleUploadStart: function(file){
            divvy.console.log("Got to handleUploadStart");
            if (!this.startTime){
                this.startTime = new Date();
            }
            divvy.console.log("Uploading ",file);
            if (this.capture_clicks){
                $("a").livequery(this.retarget_links);
            }

            if (this.photo_list){
                this.getUploadPhotoEl(file)
                    .find(".thumbnail-box a")
                    .append('  <div class="progress-bar">'
                            + '  <div class="progress-level" '
                            + '       style="height: 0px;"></div>'
                            + '</div>');
            }
            if (this.container){
                this.getFileEl(file)
                    .addClass("upload-started")
                    .find(".upload-queue-file-progress").html("starting...").end();
            }
        },
        handleUploadProgress: function(file, bytes_complete, bytes_total){
            var percent = file.percentUploaded;
            if (this.container){
                var el = this.getFileEl(file);
                var rounded_percent = Math.round(percent);
                el.find(".upload-queue-file-percent").html(rounded_percent+"%").end();
                if (bytes_complete === bytes_total){
                    el.addClass("upload-finalizing")
                        .find(".upload-queue-file-progress").html("finalizing");
                }
                this.container.find(".completion-bar").width(
                    Math.max(5, this.getTotalUploadedPercent(bytes_complete))+"%");
            }

            if (this.photo_list){
                var upload_el = this.getUploadPhotoEl(file);
                upload_el.find('.progress-level')
                    .width("100%")
                    .height(""+percent+"%")
                    .html(""+Math.round(percent)+"%");
            }
        },
        handleUploadError: function(file, error, msg){
            divvy.alert("There was an error uploading "+file.name+": "+msg);
        },
        handleUploadSuccess: function(file, responseText){
            this.totalUploadedBytes += file.size;
            this.totalUploadedCount += 1;
            var data = eval('('+responseText+')');

            this.uploadedPhotos.push(data);
            if (this.photo_list){
                var upload_photo_el = this.getUploadPhotoEl(file);
                upload_photo_el
                    .find('.thumbnail-box a')
                    .attr("href", data.photo_url)
                    .html('')
                    .end()
                    .find('.title a')
                    .attr("href", data.owner_url)
                    .html(data.owner_name)
                    .end();

                window.setTimeout(
                    function(){
                        $('<img />')
                            .attr('src', data.thumb_url)
                            .load(
                                function(){
                                    $(this).appendTo(upload_photo_el.find('.thumbnail-box a').html(''));
                                });
                    }, 1000);
            }
            if (this.container){
                this.getFileEl(file).addClass("upload-finished")
                    .find(".upload-queue-file-progress")
                    .html("finished");
                this.updateContainer();
            }
            if (this.swfUpload.getStats().files_queued > 0){
                this.swfUpload.startUpload();
            } else {
                this._processedLoop();
            }
        },
        _processedLoop: function(){
            /**
             * Checks whether photos have been processed and removes
             * the progress bar once all of them have been processed.
             */
            var self = this;
            var completed = true;
            if (this.config.checkPhotoProcessed){
                $.each(
                    this.uploadedPhotos,
                    function(){
                        completed = self.config.checkPhotoProcessed(this);
                        return completed;
                    });
            } else {
                completed = this.config.checkPhotosProcessed(this.uploadedPhotos);
            }
            if (completed){
                if (this.container){
                    var progress = this.container.find(".progress");
                    progress.fadeOut("slow", function(){progress.remove();});
                }
            } else {
                window.setTimeout(divvy.caller(this._processedLoop, this), 500);
            }

        },
        handleUploadComplete: function(){
            if (this.capture_clicks){
                $("a").expire(this.retarget_links);
                $("a.divvy-upload-retargeted").removeAttr("target");
            }
            divvy.console.log("Got to: upload_complete_handler:", arguments);
        }
    });

divvy.upload.newUploader = function(config){
    return new divvy.upload.Uploader(config);
};