(function(context,$) {

    "use strict";

    context.JK = context.JK || {};

    context.JK.ClientUpdate = function(app) {
        var self = this;
        var logger = context.JK.logger;

        var ellipsesJiggleTimer = null;

        app.clientUpdating = false;

        // updated once a download is started
        var updateSize = 0;

        function cancelUpdate(e) {
            if((e.ctrlKey || e.metaKey) && e.keyCode == 78) {
                console.log("update canceled!");
                $('#client_update').hide();
                $('#client_update_overlay').hide();
                app.clientUpdating = false;
            }
        }

        // responsible for updating the contents of the update dialog
        // as well as registering for any event handlers
        function updateClientUpdateDialog(templateId, options) {
            var template = $('#template-' + templateId).html();
            var templateHtml = context.JK.fillTemplate(template, options);

            $('#client_update .dialog-inner').html(templateHtml);

            $('#client_update').attr('data-mode', templateId);

            // assign click handlers
            if(templateId == "update-start") {

                $('body').on('keyup', cancelUpdate);

                $("#client_update a.close-application").click(function() {
                    // noop atm
                    return false;
                })

                $("#client_update a.start-update").click(function() {
                    startDownload(options.uri)
                    return false;
                })
            }
            else if(templateId == "update-downloading") {

                $('body').off('keyup', cancelUpdate);

                $("#client_update a.close-application").click(function() {
                    // noop atm
                    return false;
                })
            }

            $('#client_update').show()
            $('#client_update_overlay').show()
        }

        /***************************************/
        /******** CALLBACKS FROM BACKEND *******/
        /***************************************/
        function clientUpdateDownloadProgress(bytesReceived, bytesTotal, downloadSpeedMegSec, timeRemaining) {
            // this fires way too many times to leave in. uncomment if debugging update feature
            //logger.debug("bytesReceived: " + bytesReceived, ", bytesTotal: " + bytesTotal, ", downloadSpeed: " + downloadSpeedMegSec, ", timeRemaining: " + timeRemaining );

            bytesReceived = Number(bytesReceived)
            bytesTotal = Number(bytesTotal)
            // bytesTotal from Qt is not trust worthy; trust server's answer instead
            $('#progress-bar').width( ((bytesReceived/updateSize) * 100).toString() + "%" )
            //$("#progressbar_detail").text(parseInt(bytesReceived) + "/" + parseInt(updateSize))
        }

        function clientUpdateDownloadSuccess(updateLocation) {
            logger.debug("client update downloaded successfully to: " + updateLocation);

            updateClientUpdateDialog("update-proceeding");

            setTimeout(function() {
                // This method is synchronous, and does alot of work on a mac in particular, hanging the UI.
                // So, we do a sleep loop to make sure the UI is updated with the last message to the user, before we hang the UI
                startUpdate(updateLocation);
            }, 500);

        }

        function clientUpdateDownloadFailure(errorMsg) {
            logger.error("client update download error: " + errorMsg)

            updateClientUpdateDialog("update-error", {error_msg: "Unable to download client update.  Error reason: <br/>" + errorMsg });
        }


        function clientUpdateLaunchSuccess(updateLocation) {
            logger.debug("client update launched successfully to: " + updateLocation);

            updateClientUpdateDialog("update-restarting");
        }

        function clientUpdateLaunchFailure(errorMsg) {
            logger.error("client update launch error: " + errorMsg)

            updateClientUpdateDialog("update-error", {error_msg: "Unable to launch client updater.  Error reason: <br/>" + errorMsg});
        }

        function clientUpdateLaunchStatuses(statuses) {
            logger.debug("client update launch statuses");

            if(statuses) {
                for (var i = 0; i < statuses.length; i++) {
                    var status = statuses[i];
                }
            }
        }

        function clientUpdateLaunchStatusChange(done, status) {
            logger.debug("client update launch status change. starting=" + done + ", status=" + status);

            if(!done) {
                var $ellipses = $('<span class="ellipses">.</span>');
                $ellipses.data('count', 1);
                var $status = $('<span class="status"></span>');
                $status.text(status);
                $status.append($ellipses);
                $('#client-updater-updating').append($status);

                ellipsesJiggleTimer = setInterval(function() {
                    var count = $ellipses.data('count');
                    count++;
                    if(count > 3) {
                        count = 1;
                    }
                    $ellipses.text(Array(count + 1).join("."));
                    $ellipses.data('count', count);
                }, 500);
            }
            else {
                clearInterval(ellipsesJiggleTimer);
                $('#client-updater-updating span.status').last().css('color', 'gray').find('span.ellipses').text('...');
            }

        }
        /********************************************/
        /******** END: CALLBACKS FROM BACKEND *******/
        /********************************************/

        // if the current version doesn't not match the server version, attempt to do an upgrade
        function shouldUpdate(currentVersion, version) {
            if(version === undefined || version == null || version == "") {
                return false;
            }
            else {
                return currentVersion != version;
            }
        }

        // check if updated is needed
        function check() {

            // check kill switch before all other logic
            if(!gon.check_for_client_updates) {
                logger.debug("skipping client update because the server is telling us not to")
                return;
            }

            var product = "JamClient"
            var os = context.jamClient.GetOSAsString()
            var currentVersion = context.jamClient.ClientUpdateVersion();


            if(currentVersion == null || currentVersion.indexOf("Compiled") > -1) {
                // this is a developer build; it doesn't make much sense to do an packaged update, so skip
                logger.debug("skipping client update check because this is a development build ('" + currentVersion + "')")
                return;
            }

            // # strange client oddity: remove quotes, if found, from start and finish of version.
            if(currentVersion.indexOf('"') == 0 && currentVersion.lastIndexOf('"') == currentVersion.length -1 ) {
                currentVersion = currentVersion.substring(1, currentVersion.length - 1);
            }


            $.ajax({
                type: "GET",
                url: "/api/versioncheck?product=" + product + "&os=" + os,
                success: function(response) {
                    var version = response.version;
                    logger.debug("our client version: " + currentVersion + ", server client version: " + version);

                    // test url in lieu of having a configured server with a client-update available

                    if(shouldUpdate(currentVersion, version)) {
                        updateSize = response.size;
                        app.clientUpdating = true;

                        // test metadata in lieu of having a configured server with a client-update available
                         //updateSize = 10000;
                         //version = "1.2.3"

                        // this will update the client dialog to how it should look when an update is just starting
                        // and show it front-and-center on the screen
                        updateClientUpdateDialog("update-start", { uri : response.uri } )
                    }
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    logger.error("Unable to do a client update check against /api/versioncheck");
                }
            });
        }

        function startDownload(url) {
            logger.debug("starting client updater download from: " + url);

            updateClientUpdateDialog("update-downloading")

            context.jamClient.ClientUpdateStartDownload(url,
                "JK.ClientUpdate.DownloadProgressCallback",
                "JK.ClientUpdate.DownloadSuccessCallback",
                "JK.ClientUpdate.DownloadFailureCallback");
        }

        function startUpdate(updaterFilePath) {
            logger.debug("starting client update from: " + updaterFilePath)

            context.jamClient.ClientUpdateStartUpdate(updaterFilePath,
                "JK.ClientUpdate.LaunchUpdateSuccessCallback",
                "JK.ClientUpdate.LaunchUpdateFailureCallback",
                "JK.ClientUpdate.LaunchUpdateStatusesCallback",
                "JK.ClientUpdate.LaunchUpdateStatusChangeCallback");
        }

        function initialize() {
            context.JK.ClientUpdate.DownloadProgressCallback = clientUpdateDownloadProgress;
            context.JK.ClientUpdate.DownloadSuccessCallback = clientUpdateDownloadSuccess;
            context.JK.ClientUpdate.DownloadFailureCallback = clientUpdateDownloadFailure;
            context.JK.ClientUpdate.LaunchUpdateSuccessCallback = clientUpdateLaunchSuccess;
            context.JK.ClientUpdate.LaunchUpdateFailureCallback = clientUpdateLaunchFailure;
            context.JK.ClientUpdate.LaunchUpdateStatusesCallback = clientUpdateLaunchStatuses;
            context.JK.ClientUpdate.LaunchUpdateStatusChangeCallback = clientUpdateLaunchStatusChange;

            return self;
        }

        // Expose publics
        this.initialize = initialize;
        this.check = check;
    }

})(window,jQuery);