/**
* FtueAudioSelectionScreen
* Javascript that goes with the screen where initial
* selection of the audio devices takes place.
* Corresponds to /#ftue2
*/
(function(context,$) {

    "use strict";

    context.JK = context.JK || {};
    context.JK.FtueWizard = function(app) {
        context.JK.FtueWizard.latencyTimeout = true;
        context.JK.FtueWizard.latencyMS = Number.MAX_VALUE;
        var rest = context.JK.Rest();

        var logger = context.JK.logger;
        var jamClient = context.jamClient;
        var win32 = true;

        var deviceSetMap = {
            'audio-input': jamClient.FTUESetMusicInput,
            'audio-output': jamClient.FTUESetMusicOutput,
            'voice-chat-input': jamClient.FTUESetChatInput
        };

        var faderMap = {
            'ftue-audio-input-fader': jamClient.FTUESetInputVolume,
            'ftue-voice-input-fader': jamClient.FTUESetChatInputVolume,
            'ftue-audio-output-fader': jamClient.FTUESetOutputVolume
        };

        function latencyTimeoutCheck() {
            if (context.JK.FtueWizard.latencyTimeout) {
                jamClient.FTUERegisterLatencyCallback('');
                context.JK.app.setWizardStep("5");
            }
        }

        function afterHide(data) {
            // Unsubscribe from FTUE VU callbacks.
            jamClient.FTUERegisterVUCallbacks('','','');
        }

        function beforeShow(data) {
            var vuMeters = [
                '#ftue-audio-input-vu-left',
                '#ftue-audio-input-vu-right',
                '#ftue-voice-input-vu-left',
                '#ftue-voice-input-vu-right',
                '#ftue-audio-output-vu-left',
                '#ftue-audio-output-vu-right'
            ];
            $.each(vuMeters, function() {
                context.JK.VuHelpers.renderVU(this,
                    {vuType:"horizontal", lightCount: 12, lightWidth: 15, lightHeight: 3});
            });

            var faders = context._.keys(faderMap);
            $.each(faders, function() {
                var fid = this;
                context.JK.FaderHelpers.renderFader('#' + fid,
                    {faderId: fid, faderType:"horizontal", width:163});
                context.JK.FaderHelpers.subscribe(fid, faderChange);
            });
        }

        function afterShow(data) {}

        function faderChange(faderId, newValue, dragging) {
            var setFunction = faderMap[faderId];
            // TODO - using hardcoded range of -80 to 20 for output levels.
            var mixerLevel = newValue - 80; // Convert our [0-100] to [-80 - +20] range
            setFunction(mixerLevel);
        }

        function settingsInit() {
            jamClient.FTUEInit();
            setLevels(0);
            // Always reset the driver select box to "Choose..." which forces everything
            // to sync properly when the user reselects their driver of choice.
            // VRFS-375 and VRFS-561
            $('[layout-wizard="ftue"] [layout-wizard-step="2"] .asio-settings .settings-driver select').val("");
        }

        function setLevels(db) {
            if (db < -80 || db > 20) {
                throw ("BUG! ftue.js:setLevels db arg must be between -80 and 20");
            }
            var trackIds = jamClient.SessionGetIDs();
            var controlStates = jamClient.SessionGetControlState(trackIds);
            $.each(controlStates, function(index, value) {
                context.JK.Mixer.fillTrackVolume(value, false);
                // Default input/output to 0 DB
                context.trackVolumeObject.volL = db;
                context.trackVolumeObject.volR = db;
                jamClient.SessionSetControlState(trackIds[index]);
            });
            $.each(context._.keys(faderMap), function(index, faderId) {
                // faderChange takes a value from 0-100
                var $fader = $('[fader-id="' + faderId + '"]');
                context.JK.FaderHelpers.setHandlePosition($fader, db + 80);
            });
        }

        function testComplete() {
            logger.debug("Test complete");
            var latencyMS = context.JK.FtueWizard.latencyMS;
            var ftueSucceeded = latencyMS <= 20;
            if (ftueSucceeded) {
                logger.debug(latencyMS + " is <= 20. Setting FTUE status to true");
                ftueSave(true); // Save the profile
                context.jamClient.FTUESetStatus(true); // No FTUE wizard next time
                rest.userCertifiedGear({success:true});

                // notify anyone curious about how it went
                $('div[layout-id=ftue]').trigger('ftue_success');
            }
            else {
                rest.userCertifiedGear({success:false, reason:"latency=" + latencyMS});
            }

            updateGauge();
        }

        function updateGauge() {
            var latencyMS = context.JK.FtueWizard.latencyMS;
            // Round to 2 decimal places
            latencyMS = (Math.round(latencyMS * 100)) / 100;
            logger.debug("Latency Value: " + latencyMS);
            if (latencyMS > 20) {
                $('#ftue-latency-congrats').hide();
                $('#ftue-latency-fail').show();
            } else {
                $('#ftue-latency-ms').html(latencyMS);
                $('#ftue-latency-congrats').show();
                $('#ftue-latency-fail').hide();
                if (latencyMS <= 10) {
                    $('[layout-wizard-step="6"] .btnHelp').hide();
                    $('[layout-wizard-step="6"] .btnRepeat').hide();
                }
            }
            setNeedleValue(latencyMS);
        }

        // Function to calculate an absolute value and an absolute value range into
        // a number of degrees on a circualar "speedometer" gauge. The 0 degrees value
        // points straight up to the middle of the real-world value range.
        // Arguments:
        //   value: The real-world value (e.g. 20 milliseconds)
        //   minValue: The real-world minimum value (e.g. 0 milliseconds)
        //   maxValue: The real-world maximum value (e.g. 40 milliseconds)
        //   degreesUsed: The number of degrees used to represent the full real-world
        //                range. 360 would be the entire circle. 270 would be 3/4ths
        //                of the circle. The unused gap will be at the bottom of the
        //                gauge.
        //               (e.g. 300)
        function degreesFromRange(value, minValue, maxValue, degreesUsed) {
            if (value > maxValue) { value = maxValue; }
            if (value < minValue) { value = minValue; }
            var range = maxValue-minValue;
            var floatVal = value/range;
            var degrees = Math.round(floatVal * degreesUsed);
            degrees -= Math.round(degreesUsed/2);
            if (degrees < 0) {
                degrees += 360;
            }
            return degrees;
        }

        // Given a number of MS, and assuming the gauge has a range from
        // 0 to 40 ms. Update the gauge to the proper value.
        function setNeedleValue(ms) {
            logger.debug("setNeedleValue: " + ms);
            var deg = degreesFromRange(ms, 0, 40, 300);

            // Supporting Firefix, Chrome, Safari, Opera and IE9+.
            // Should we need to support < IE9, this will need more work
            // to compute the matrix transformations in those browsers -
            // and I don't believe they support transparent PNG graphic
            // rotation, so we'll have to change the needle itself.
            var css = {
                //"behavior":"url(-ms-transform.htc)",
                /* Firefox */
                "-moz-transform":"rotate(" + deg + "deg)",
                /* Safari and Chrome */
                "-webkit-transform":"rotate(" + deg + "deg)",
                /* Opera */
                "-o-transform":"rotate(" + deg + "deg)",
                /* IE9 */
                "-ms-transform":"rotate(" + deg + "deg)"
                /* IE6,IE7 */
                //"filter": "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)",
                /* IE8 */
                //"-ms-filter": '"progid:DXImageTransform.Microsoft.Matrix(SizingMethod=\'auto expand\', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)"'
            };

            $('#ftue-latency-numerical').html(ms);
            $('#ftue-latency-needle').css(css);

        }

        function testLatency() {
            // we'll just register for call back right here and unregister in the callback.
            context.JK.FtueWizard.latencyTimeout = true;
            var cbFunc = 'JK.ftueLatencyCallback';
            logger.debug("Registering latency callback: " + cbFunc);
            jamClient.FTUERegisterLatencyCallback('JK.ftueLatencyCallback');
            var now = new Date();
            logger.debug("Starting Latency Test..." + now);
            context.setTimeout(latencyTimeoutCheck, 300 * 1000); // Timeout to 5 minutes
            jamClient.FTUEStartLatency();
        }

        function openASIOControlPanel(evt) {
            if (win32) {
                logger.debug("Calling FTUEOpenControlPanel()");
                jamClient.FTUEOpenControlPanel();
            }
        }

        function asioResync(evt) {
            jamClient.FTUERefreshDevices();
            ftueSave(false);
        }

        function ftueSave(persist) {
            // Explicitly set inputs and outputs to dropdown values
            // before save as the client seems to want this on changes to
            // things like frame size, etc..
            var $audioSelects = $('[layout-wizard-step="2"] .settings-controls select');
            $.each($audioSelects, function(index, value) {
                var $select = $(value);
                setAudioDevice($select);
            });
            if (musicInAndOutSet()) {

                // If there is no voice-chat-input selected, let the back-end know
                // that we're using music for voice-chat.
                if ($('[layout-wizard-step="2"] select[data-device="voice-chat-input"]').val()) {
                    // Voice input selected
                    jamClient.TrackSetChatEnable(true);
                } else {
                    // No voice input selected.
                    jamClient.TrackSetChatEnable(false);
                }

                logger.debug("Calling FTUESave(" + persist + ")");
                var response = jamClient.FTUESave(persist);
                setLevels(0);
                if (response) {
                    logger.warn(response);
                    // TODO - we may need to do something about errors on save.
                    // per VRFS-368, I'm hiding the alert, and logging a warning.
                    // context.alert(response);
                }
            } else {
                logger.debug("Aborting FTUESave as we need input + output selected.");
            }
        }

        function setAsioFrameSize(evt) {
            var val = parseFloat($(evt.currentTarget).val(),10);
            if (isNaN(val)) {
                return;
            }
            logger.debug("Calling FTUESetFrameSize(" + val + ")");
            jamClient.FTUESetFrameSize(val);
            ftueSave(false);
        }
        function setAsioInputLatency(evt) {
            var val = parseInt($(evt.currentTarget).val(),10);
            if (isNaN(val)) {
                return;
            }
            logger.debug("Calling FTUESetInputLatency(" + val + ")");
            jamClient.FTUESetInputLatency(val);
            ftueSave(false);
        }
        function setAsioOutputLatency(evt) {
            var val = parseInt($(evt.currentTarget).val(),10);
            if (isNaN(val)) {
                return;
            }
            logger.debug("Calling FTUESetOutputLatency(" + val + ")");
            jamClient.FTUESetOutputLatency(val);
            ftueSave(false);
        }

        function videoLinkClicked(evt) {
            var myOS = jamClient.GetOSAsString();
            var link;
            if (myOS === 'MacOSX') {
                link = $(evt.currentTarget).attr('external-link-mac');
            } else if (myOS === 'Win32') {
                link = $(evt.currentTarget).attr('external-link-win');
            }
            if (link) {
                context.jamClient.OpenSystemBrowser(link);
            }
        }

        function events() {
            $('.ftue-video-link').hover(
                function(evt) { // handlerIn
                    $(this).addClass('hover');
                },
                function(evt) { // handlerOut
                    $(this).removeClass('hover');
                }
            );
            $('.ftue-video-link').on('click', videoLinkClicked);
            $('[layout-wizard-step="2"] .settings-driver select').on('change', audioDriverChanged);
            $('[layout-wizard-step="2"] .settings-controls select').on('change', audioDeviceChanged);
            $('#btn-asio-control-panel').on('click', openASIOControlPanel);
            $('#btn-asio-resync').on('click', asioResync);
            $('#asio-framesize').on('change', setAsioFrameSize);
            $('#asio-input-latency').on('change', setAsioInputLatency);
            $('#asio-output-latency').on('change', setAsioOutputLatency);
        }

        /**
        * This function loads the available audio devices from jamClient, and
        * builds up the select dropdowns in the audio-settings step of the FTUE wizard.
        */
        function loadAudioDevices() {
            var funcs = [
                jamClient.FTUEGetMusicInputs,
                jamClient.FTUEGetChatInputs,
                jamClient.FTUEGetMusicOutputs
            ];
            var selectors = [
                '[layout-wizard-step="2"] .audio-input select',
                '[layout-wizard-step="2"] .voice-chat-input select',
                '[layout-wizard-step="2"] .audio-output select'
            ];
            var optionsHtml = '';
            var deviceOptionFunc = function(deviceKey, index, list) {
                optionsHtml += '<option title="' + devices[deviceKey] + '" value="' + deviceKey + '">' +
                    devices[deviceKey] + '</option>';
            };
            for (var i=0; i<funcs.length; i++) {
                optionsHtml = '<option selected="selected" value="">Choose...</option>';

                var devices = funcs[i](); // returns hash of device id: device name
                var $select = $(selectors[i]);
                $select.empty();
                var sortedDeviceKeys = context._.keys(devices).sort();
                context._.each(sortedDeviceKeys, deviceOptionFunc);
                $select.html(optionsHtml);
                $select.removeAttr("disabled");
                $('[layout-wizard-step="2"] .settings-asio select').removeAttr("disabled");
                // Set selects to lowest possible values to start:
                $('#asio-framesize').val('2.5').change();
                $('#asio-input-latency').val('0').change();
                $('#asio-output-latency').val('0').change();
            }
        }

        /**
        * Load available drivers and populate the driver select box.
        */
        function loadAudioDrivers() {

            var drivers = jamClient.FTUEGetDevices();

            var driverOptionFunc = function(driverKey, index, list) {
                optionsHtml += '<option title="' + drivers[driverKey] + '"value="' + driverKey + '">' +
                    drivers[driverKey] + '</option>';
            };

            var optionsHtml = '<option selected="selected" value="">Choose...</option>';
            var $select = $('[layout-wizard-step="2"] .settings-driver select');
            $select.empty();
            var sortedDeviceKeys = context._.keys(drivers).sort();
            context._.each(sortedDeviceKeys, driverOptionFunc);
            $select.html(optionsHtml);
        }

        function audioDriverChanged(evt) {
            var $select = $(evt.currentTarget);
            var driverId = $select.val();
            jamClient.FTUESetMusicDevice(driverId);
            loadAudioDevices();
            setAsioSettingsVisibility();
        }

        function audioDeviceChanged(evt) {
            var $select = $(evt.currentTarget);
            setAudioDevice($select);
            if (musicInAndOutSet()) {
                ftueSave(false);
                setVuCallbacks();
            }
        }

        function setAudioDevice($select) {
            var device = $select.data('device');
            var deviceId = $select.val();
            // Note: We always set, even on the "Choose" value of "", which clears
            // the current setting.
            var setFunction = deviceSetMap[device];
            setFunction(deviceId);
        }

        /**
        * Return a boolean indicating whether both the MusicInput
        * and MusicOutput devices are set.
        */
        function musicInAndOutSet() {
            var audioInput = $('[layout-wizard-step="2"] .audio-input select').val();
            var audioOutput = $('[layout-wizard-step="2"] .audio-output select').val();
            return (audioInput && audioOutput);
        }

        function setVuCallbacks() {
            jamClient.FTUERegisterVUCallbacks(
                "JK.ftueAudioOutputVUCallback",
                "JK.ftueAudioInputVUCallback",
                "JK.ftueChatInputVUCallback"
            );
            jamClient.SetVURefreshRate(200);
        }

        function setAsioSettingsVisibility() {
            logger.debug("jamClient.FTUEHasControlPanel()=" + jamClient.FTUEHasControlPanel());
            if (jamClient.FTUEHasControlPanel()) {
                logger.debug("Showing ASIO button");
                $('#btn-asio-control-panel').show();
            }
            else {
                logger.debug("Hiding ASIO button");
                $('#btn-asio-control-panel').hide();
            }
        }

        function initialize() {
            // If not on windows, hide ASIO settings
            if (jamClient.GetOSAsString() != "Win32") {
                logger.debug("Not on Win32 - modifying UI for Mac/Linux");
                win32 = false;
                $('[layout-wizard-step="2"] p[os="win32"]').hide();
                $('[layout-wizard-step="2"] p[os="mac"]').show();
                $('#btn-asio-control-panel').hide();
                $('[layout-wizard-step="2"] .settings-controls select').removeAttr("disabled");
                loadAudioDevices();
            }

            setAsioSettingsVisibility();

            events();
            var dialogBindings = { 'beforeShow': beforeShow,
                'afterShow': afterShow, 'afterHide': afterHide };
            app.bindDialog('ftue', dialogBindings);
            app.registerWizardStepFunction("2", settingsInit);
            app.registerWizardStepFunction("4", testLatency);
            app.registerWizardStepFunction("6", testComplete);
            loadAudioDrivers();
        }

        // Expose publics
        this.initialize = initialize;

        // Expose degreesFromRange outside for testing
        this._degreesFromRange = degreesFromRange;

        return this;
    };



    // Common VU updater taking a dbValue (-80 to 20) and a CSS selector for the VU.
    context.JK.ftueVUCallback = function(dbValue, selector) {
        // Convert DB into a value from 0.0 - 1.0
        var floatValue = (dbValue + 80) / 100;
        context.JK.VuHelpers.updateVU(selector, floatValue);
    };

    context.JK.ftueAudioInputVUCallback = function(dbValue) {
        context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-left');
        context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-right');
    };
    context.JK.ftueAudioOutputVUCallback = function(dbValue) {
        context.JK.ftueVUCallback(dbValue, '#ftue-audio-output-vu-left');
        context.JK.ftueVUCallback(dbValue, '#ftue-audio-output-vu-right');
    };
    context.JK.ftueChatInputVUCallback = function(dbValue) {
        context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-left');
        context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-right');
    };

    // Latency Callback
    context.JK.ftueLatencyCallback = function(latencyMS) {
        // We always show gauge screen if we hit this.
        // Clear out the 'timeout' variable.
        context.JK.FtueWizard.latencyTimeout = false;
        var now = new Date();
        context.console.debug("ftueLatencyCallback: " + now);
        context.JK.FtueWizard.latencyMS = latencyMS;

        // Unregister callback:
        context.jamClient.FTUERegisterLatencyCallback('');
        // Go to 'congrats' screen -- although latency may be too high.
        context.JK.app.setWizardStep("6");
    };


 })(window,jQuery);