(function (context, $) {

  "use strict";

  context.JK = context.JK || {};
  context.JK.ConfigureTrackDialog = function (app, myTracks, sessionId, sessionModel) {
    var logger = context.JK.logger;
    var myTrackCount;

    var ASSIGNMENT = context.JK.ASSIGNMENT;
    var VOICE_CHAT = context.JK.VOICE_CHAT;

    var instrument_array = [];

    // dialog variables
    var inputUnassignedList = [];
    var track1AudioInputChannels = [];
    var track2AudioInputChannels = [];

    var outputUnassignedList = [];
    var outputAssignedList = [];

    var chatUnassignedList = [];
    var chatAssignedList = [];

    var chatOtherUnassignedList = [];
    var chatOtherAssignedList = [];

    var devices = [];
    var originalDeviceId;
    var originalVoiceChat;

    var configure_audio_instructions = {
      "Win32": "Choose the audio device you would like to use for this session. If needed, use arrow buttons to assign audio inputs " +
        "to your tracks, to indicate what instrument you are playing on each track, and to assign audio outputs for listening. " +
        "If you want to use a new audio device you have not tested/certified for latency using JamKazam, click the Add New Audio " +
        "Gear button to test that device.",

      "MacOSX": "Choose the audio device you would like to use for this session. If needed, use arrow buttons to assign audio inputs " +
        "to your tracks, to indicate what instrument you are playing on each track, and to assign audio outputs for listening. " +
        "If you want to use a new audio device you have not tested/certified for latency using JamKazam, click the Add New Audio " +
        "Gear button to test that device.",

      "Unix": "Choose the audio device you would like to use for this session. If needed, use arrow buttons to assign audio inputs " +
        "to your tracks, to indicate what instrument you are playing on each track, and to assign audio outputs for listening. " +
        "If you want to use a new audio device you have not tested/certified for latency using JamKazam, click the Add New Audio " +
        "Gear button to test that device."
    };

    var configure_voice_instructions = "If you are using a microphone to capture your instrumental or vocal audio, you can simply use that mic " +
      "for both music and chat. Otherwise, choose a device to use for voice chat, and use arrow buttons to " +
      "select an input on that device.";

    function events() {

      // Music Audio Tab
      var $tabConfigureAudio = $('#tab-configure-audio');
      $tabConfigureAudio.unbind("click");
      $tabConfigureAudio.click(function () {
        // validate voice chat settings
        if (validateVoiceChatSettings(true)) {
          showMusicAudioPanel(false);
        }
      });

      // Voice Chat Tab
      var $tabConfigureVoice = $('#tab-configure-voice');
      $tabConfigureVoice.unbind("click");
      $tabConfigureVoice.click(function () {
        // validate audio settings
        if (validateAudioSettings(true)) {
          showVoiceChatPanel(false);
        }
      });

      // Track 1 Add
      var $imgTrack1Add = $('#img-track1-input-add');
      $imgTrack1Add.unbind("click");
      $imgTrack1Add.click(function () {
        var $unusedMusicInputs = $('#audio-inputs-unused > option:selected');
        _handleTrackInputAdd($unusedMusicInputs, '#track1-input');
      });

      // Track 2 Add
      var $imgTrack2Add = $('#img-track2-input-add');
      $imgTrack2Add.unbind("click");
      $imgTrack2Add.click(function () {
        var $unusedMusicInputs = $('#audio-inputs-unused > option:selected');
        _handleTrackInputAdd($unusedMusicInputs, '#track2-input');
      });

      // Track 1 Remove
      var $imgTrack1Remove = $('#img-track1-input-remove');
      $imgTrack1Remove.unbind("click");
      $imgTrack1Remove.click(function () {
        _handleTrackInputRemove('#track1-input');
      });

      // Track 2 Remove
      var $imgTrack2Remove = $('#img-track2-input-remove');
      $imgTrack2Remove.unbind("click");
      $imgTrack2Remove.click(function () {
        _handleTrackInputRemove('#track2-input');
      });

      // Audio Output Add
      var $imgAudioOutputAdd = $('#img-audio-output-add');
      $imgAudioOutputAdd.unbind("click");
      $imgAudioOutputAdd.click(function () {
        var $unusedAudioOutputs = $('#audio-output-unused > option:selected');
        $unusedAudioOutputs.remove().appendTo('#audio-output-selection');
      });

      // Audio Output Remove
      var $imgAudioOutputRemove = $('#img-audio-output-remove');
      $imgAudioOutputRemove.unbind("click");
      $imgAudioOutputRemove.click(function () {
        var $usedAudioOutputs = $('#audio-output-selection > option:selected');
        $usedAudioOutputs.remove().appendTo('#audio-output-unused');
      });

      // Voice Chat Add
      var $imgVoiceAdd = $('#img-voice-input-add');
      $imgVoiceAdd.unbind("click");
      $imgVoiceAdd.click(function () {
        var $unusedVoiceInputs = $('#voice-inputs-unused > option:selected');
        _handleVoiceInputAdd($unusedVoiceInputs);
      });

      // Voice Chat Remove
      var $imgVoiceRemove = $('#img-voice-input-remove');
      $imgVoiceRemove.unbind("click");
      $imgVoiceRemove.click(function () {
        var $usedVoiceInputs = $("#voice-inputs-selection > option:selected");
        _handleVoiceInputRemove($usedVoiceInputs);
      });

      $('#audio-drivers').unbind("change");
      $('#audio-drivers').change(function () {
        audioDriverChanged();
      });

      $('#voice-chat-type').unbind("change");
      $('#voice-chat-type').change(function () {
        voiceChatChanged();
      });

      $('#btn-driver-settings').unbind("click");
      $('#btn-driver-settings').click(function () {
        context.jamClient.TrackOpenControlPanel();
      });

      $('#btn-cancel-new-audio').unbind("click");
      $('#btn-cancel-new-audio').click(context.JK.showOverlay);

      $('#btn-error-ok').click(context.JK.showOverlay);

      $('#btn-save-settings').unbind("click");
      $('#btn-save-settings').click(saveSettings);

      $('#btn-cancel-settings').unbind("click");
      $('#btn-cancel-settings').click(cancelSettings);
    }

    function _handleTrackInputAdd($selectedMusicInputs, selector) {
      $selectedMusicInputs.each(function () {
        var deviceId = this.value;
        var description = this.text;
        $(this).remove().appendTo(selector);

        // if this input exists in the Voice Chat unused box, remove it
        var $voiceChatUnused = $('#voice-inputs-unused > option[value="' + deviceId + '"]');
        if ($voiceChatUnused.length > 0) {
          logger.debug("Removing " + deviceId + " from Voice Chat Unused");
          $voiceChatUnused.remove();
        }
      });

      _syncVoiceChatType();
    }

    function _handleTrackInputRemove(trackSelector) {
      trackSelector = trackSelector + ' > option:selected';
      $(trackSelector).each(function () {
        var $removedInput = $(this).remove();
        var $cloneAudio = $removedInput.clone(true, true);
        var $cloneChat = $removedInput.clone(true, true);

        $cloneAudio.appendTo('#audio-inputs-unused');

        // add it to the unused Voice Chat box
        if ($('#voice-chat-type').val() == VOICE_CHAT.CHAT) {
          $cloneChat.appendTo('#voice-inputs-unused');
        }
      });

      _syncVoiceChatType();
    }

    function _syncVoiceChatType() {
      var $option1 = $('#voice-chat-type > option[value="1"]');

      // remove option 1 from voice chat type dropdown if no music (based on what's unused on the Music Audio tab) or chat inputs are available
      if ($('#audio-inputs-unused > option').size() === 0 && chatOtherUnassignedList.length === 0 && chatOtherAssignedList.length === 0) {
        $option1.remove();
      }
      else {
        // make sure it's not already in list before adding back
        if ($option1.length === 0) {
          logger.debug("Adding Option 1 back to Voice Chat dropdown.");
          $('#voice-chat-type').append('<option value="1">Use an input from the list below for chat</option>');
        }
      }
    }

    function _handleVoiceInputAdd($selectedVoiceInputs) {
      $selectedVoiceInputs.each(function () {
        var id = this.value;

        // if this input is in the unused track input box, remove it
        var $unusedMusic = $('#audio-inputs-unused > option[value="' + id + '"]');
        if ($unusedMusic.length > 0) {
          $unusedMusic.remove();
        }
        $(this).remove().appendTo('#voice-inputs-selection');
      });
    }

    function _handleVoiceInputRemove($selectedVoiceInputs) {
      $selectedVoiceInputs.each(function () {
        var $removedInput = $(this).remove();
        var $cloneAudio = $removedInput.clone(true, true);
        var $cloneChat = $removedInput.clone(true, true);

        $cloneChat.appendTo('#voice-inputs-unused');

        // add it to the unused Music Input box if the selected input is not type "chat"
        if (!isChatInput(this.value)) {
          $cloneAudio.appendTo('#audio-inputs-unused');
        }
      });
    }

    function isChatInput(id) {
      // copy the arrays
      var chatOtherUnassignedListCopy = chatOtherUnassignedList;
      var chatOtherAssignedListCopy = chatOtherAssignedList;

      // is this input in the unassigned list?
      chatOtherUnassignedListCopy = $.grep(chatOtherUnassignedListCopy, function (n, i) {
        return n.chat && n.id === id;
      });

      // is this input in the assigned list?
      chatOtherAssignedListCopy = $.grep(chatOtherAssignedListCopy, function (n, i) {
        return n.chat && n.id === id;
      });

      logger.debug("chatOtherUnassignedListCopy=" + JSON.stringify(chatOtherUnassignedListCopy));
      logger.debug("chatOtherAssignedListCopy=" + JSON.stringify(chatOtherAssignedListCopy));

      return chatOtherUnassignedListCopy.length > 0 || chatOtherAssignedListCopy.length > 0;
    }

    function audioDriverChanged() {

      context.jamClient.TrackSetMusicDevice($('#audio-drivers').val());

      logger.debug("Called TrackSetMusicDevice with " + $('#audio-drivers').val());

      context.jamClient.TrackLoadAssignments();
      initDialogData();

      // refresh dialog
      showVoiceChatPanel(true);
      showMusicAudioPanel(true);
    }

    function voiceChatChanged() {
      var voiceChatVal = $('#voice-chat-type').val();

      logger.debug("voiceChatVal=" + voiceChatVal);

      if (voiceChatVal == VOICE_CHAT.NO_CHAT) {
        logger.debug("VOICE_CHAT.NO_CHAT");
        _addSelectedVoiceInputsToMusicInputs();

        $('#voice-inputs-unused').empty();
        $('#voice-inputs-selection').empty();
      }
      else if (voiceChatVal == VOICE_CHAT.CHAT) {
        logger.debug("VOICE_CHAT.CHAT");

        $('#voice-inputs-unused').empty();
        $('#voice-inputs-selection').empty();

        // add the chat inputs (unassigned and assigned) to the unused box to force the user to select again
        context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-unused'), chatOtherUnassignedList, "id", "name", -1);
        context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-unused'), chatOtherAssignedList, "id", "name", -1);

        // add each unused music input if it doesn't already exist
        $('#audio-inputs-unused > option').each(function () {
          if ($('#voice-inputs-unused > option[value="' + this.value + '"]').length === 0) {
            logger.debug("Appending " + this.value + " to the unused voice input box.");
            $('#voice-inputs-unused').append('<option value="' + this.value + '">' + this.text + '</option>');
          }
        });
      }
    }

    function _addSelectedVoiceInputsToMusicInputs() {
      $('#voice-inputs-selection > option').each(function () {
        // if this input is not already in the available music inputs box and the selected input is not chat input, add
        // it to the unused music inputs box
        if ($('#audio-inputs-unused > option[value="' + this.value + '"]').length === 0 && !isChatInput(this.value)) {
          logger.debug("Appending " + this.value + " to the unused audio input box.");
          $('#audio-inputs-unused').append('<option value="' + this.value + '">' + this.text + '</option>');
        }
      });
    }

    function configureDriverSettingsButton() {
      if (context.jamClient.TrackHasControlPanel()) {
        $('#btn-driver-settings').show();
      }
      else {
        $('#btn-driver-settings').hide();
      }
    }

    function showMusicAudioPanel(refreshLists) {
      _setInstructions('audio');
      _activateTab('audio');

      if (refreshLists) {
        $('#audio-drivers').empty();

        // determine correct music device to preselect
        var deviceId = context.jamClient.TrackGetMusicDeviceID();
        logger.debug("deviceId = " + deviceId);

        // load Audio Driver dropdown
        devices = context.jamClient.TrackGetDevices();
        logger.debug("Called TrackGetDevices with response " + JSON.stringify(devices));
        var keys = Object.keys(devices);

        for (var i = 0; i < keys.length; i++) {
          var template = $('#template-option').html();
          var isSelected = "";
          if (keys[i] === deviceId) {
            isSelected = "selected";
          }
          var html = context.JK.fillTemplate(template, {
            value: keys[i],
            label: devices[keys[i]],
            selected: isSelected
          });

          $('#audio-drivers').append(html);
        }

        context.JK.dropdown($('#audio-drivers'));

        if (deviceId === '') {
          context.jamClient.TrackSetMusicDevice($('#audio-drivers').val());
        }

        configureDriverSettingsButton();

        $('#audio-inputs-unused').empty();
        $('#track1-input').empty();
        $('#track1-instrument').empty();
        $('#track2-input').empty();
        $('#track2-instrument').empty();
        $('#audio-output-unused').empty();
        $('#audio-output-selection').empty();

        _initMusicTabData();

        // load Unused Inputs
        context.JK.loadOptions($('#template-option').html(), $('#audio-inputs-unused'), inputUnassignedList, "id", "name", -1);

        // load Track 1 Input(s)
        context.JK.loadOptions($('#template-option').html(), $('#track1-input'), track1AudioInputChannels, "id", "name", -1);

        // load Track 1 Instrument
        var current_instrument = context.jamClient.TrackGetInstrument(ASSIGNMENT.TRACK1);

        // if no instrument is stored on the backend, the user is opening this dialog for the first time after FTUE;
        // initialize to the user's first instrument
        if (current_instrument === 0) {
          if (context.JK.userMe.instruments && context.JK.userMe.instruments.length > 0) {
            var instrument_desc = context.JK.userMe.instruments[0].description;
            current_instrument = context.JK.server_to_client_instrument_map[instrument_desc].client_id;
          }
        }

        context.JK.loadOptions($('#template-option').html(), $('#track1-instrument'), instrument_array, "id", "description", current_instrument);

        // load Track 2 Input(s)
        context.JK.loadOptions($('#template-option').html(), $('#track2-input'), track2AudioInputChannels, "id", "name", -1);

        // load Track 2 Instrument
        current_instrument = context.jamClient.TrackGetInstrument(ASSIGNMENT.TRACK2);
        context.JK.loadOptions($('#template-option').html(), $('#track2-instrument'), instrument_array, "id", "description", current_instrument);

        // load Unused Outputs
        context.JK.loadOptions($('#template-option').html(), $('#audio-output-unused'), outputUnassignedList, "id", "name", -1);

        // load Session Audio Output
        context.JK.loadOptions($('#template-option').html(), $('#audio-output-selection'), outputAssignedList, "id", "name", -1);
      }
    }

    function showVoiceChatPanel(refreshLists) {
      _setInstructions('voice');
      _activateTab('voice');

      if (refreshLists) {
        $('#voice-inputs-unused').empty();
        $('#voice-inputs-selection').empty();

        _initVoiceChatTabData();

        var chatOption = $('#voice-chat-type').val();

        if (chatOption == VOICE_CHAT.CHAT) {
          context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-unused'), chatUnassignedList, "id", "name", -1);
          context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-selection'), chatAssignedList, "id", "name", -1);

          context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-unused'), chatOtherUnassignedList, "id", "name", -1);
          context.JK.loadOptions($('#template-option').html(), $('#voice-inputs-selection'), chatOtherAssignedList, "id", "name", -1);
        }

        // disable inputs
        else if (chatOption == VOICE_CHAT.NO_CHAT) {
        }
      }
    }

    function _activateTab(type) {
      if (type === 'voice') {
        $('div[tab-id="music-audio"]').hide();
        $('div[tab-id="voice-chat"]').show();

        $('#tab-configure-audio').removeClass('selected');
        $('#tab-configure-voice').addClass('selected');
      }
      else {
        $('div[tab-id="music-audio"]').show();
        $('div[tab-id="voice-chat"]').hide();

        $('#tab-configure-audio').addClass('selected');
        $('#tab-configure-voice').removeClass('selected');
      }
    }

    function initDialogData() {
      _initMusicTabData();
      _initVoiceChatTabData();
    }

    function _initMusicTabData() {
      inputUnassignedList = _loadList(ASSIGNMENT.UNASSIGNED, true, false);
      //logger.debug("inputUnassignedList=" + JSON.stringify(inputUnassignedList));

      track1AudioInputChannels = _loadList(ASSIGNMENT.TRACK1, true, false);
      //logger.debug("track1AudioInputChannels=" + JSON.stringify(track1AudioInputChannels));

      track2AudioInputChannels = _loadList(ASSIGNMENT.TRACK2, true, false);
      //logger.debug("track2AudioInputChannels=" + JSON.stringify(track2AudioInputChannels));

      outputUnassignedList = _loadList(ASSIGNMENT.UNASSIGNED, false, false);
      outputAssignedList = _loadList(ASSIGNMENT.OUTPUT, false, false);
    }

    function _initVoiceChatTabData() {
      chatUnassignedList = _loadList(ASSIGNMENT.UNASSIGNED, true, false);
      //logger.debug("chatUnassignedList=" + JSON.stringify(chatUnassignedList));

      chatAssignedList = _loadList(ASSIGNMENT.CHAT, true, false);
      //logger.debug("chatAssignedList=" + JSON.stringify(chatAssignedList));

      chatOtherUnassignedList = _loadList(ASSIGNMENT.UNASSIGNED, true, true);
      //logger.debug("chatOtherUnassignedList=" + JSON.stringify(chatOtherUnassignedList));

      chatOtherAssignedList = _loadList(ASSIGNMENT.CHAT, true, true);
      //logger.debug("chatOtherAssignedList=" + JSON.stringify(chatOtherAssignedList));
    }

    // TODO: copied in addTrack.js - refactor to common place
    function _loadList(assignment, input, chat) {
      var list = [];

      // get data needed for listboxes
      var channels = context.jamClient.TrackGetChannels();

      var musicDevices = context.jamClient.TrackGetMusicDeviceNames(input);

      // SEE loadList function in TrackAssignGui.cpp of client code
      $.each(channels, function (index, val) {

        if (input !== val.input) {
          return;
        }

        var currAssignment = context.jamClient.TrackGetAssignment(val.id, val.input);
        if (assignment !== currAssignment) {
          return;
        }

        // logger.debug("channel id=" + val.id + ", channel input=" + val.input + ", channel assignment=" + currAssignment +
        //     ", channel name=" + val.name + ", channel type=" + val.device_type + ", chat=" + val.chat);

        var os = context.jamClient.GetOSAsString();
        if (os === context.JK.OS.WIN32) {
          if (chat && ($.inArray(val.device_id, musicDevices) > -1 || context.jamClient.TrackIsMusicDeviceType(val.device_type))) {
            return;
          }
        }
        else {
          if (chat && ($.inArray(val.device_id, musicDevices) > -1 || !context.jamClient.TrackIsMusicDeviceType(val.device_type))) {
            return;
          }
        }

        if (!chat && $.inArray(val.device_id, musicDevices) === -1) {
          return;
        }

        if ((chat && !val.chat) || (!chat && val.chat)) {
          return;
        }

        list.push(val);
      });

      return list;
    }

    function saveSettings() {
      if (!context.JK.verifyNotRecordingForTrackChange(app)) {
        return;
      }

      if (!validateAudioSettings(false)) {
        return;
      }

      if (!validateVoiceChatSettings(false)) {
        return;
      }

      saveAudioSettings();
      saveVoiceChatSettings();

      context.jamClient.TrackSaveAssignments();

      originalDeviceId = $('#audio-drivers').val();
      app.layout.closeDialog('configure-audio');
    }

    function saveAudioSettings() {

      context.jamClient.TrackSetMusicDevice($('#audio-drivers').val());

      // UNASSIGNED INPUTS
      $('#audio-inputs-unused > option').each(function () {
        logger.debug("Marking " + this.value + " as unassigned input.");
        context.jamClient.TrackSetAssignment(this.value, true, ASSIGNMENT.UNASSIGNED);
      });

      // TRACK 1 INPUTS
      $('#track1-input > option').each(function () {
        logger.debug("Saving track 1 input = " + this.value);
        context.jamClient.TrackSetAssignment(this.value, true, ASSIGNMENT.TRACK1);
      });

      // TRACK 1 INSTRUMENT
      var instrumentVal = $('#track1-instrument').val();
      var instrumentText = $('#track1-instrument > option:selected').text().toLowerCase();

      logger.debug("Saving track 1 instrument = " + instrumentVal);
      context.jamClient.TrackSetInstrument(ASSIGNMENT.TRACK1, instrumentVal);


      // TRACK 2 INPUTS
      var track2Selected = false;
      $('#track2-input > option').each(function () {
        track2Selected = true;
        logger.debug("Saving track 2 input = " + this.value);
        context.jamClient.TrackSetAssignment(this.value, true, ASSIGNMENT.TRACK2);
      });

      if (track2Selected) {
        // TRACK 2 INSTRUMENT
        instrumentVal = $('#track2-instrument').val();
        instrumentText = $('#track2-instrument > option:selected').text().toLowerCase();

        logger.debug("Saving track 2 instrument = " + instrumentVal);
        context.jamClient.TrackSetInstrument(ASSIGNMENT.TRACK2, instrumentVal);
      }
      else {
        // track 2 was removed
        if (myTrackCount === 2) {
          logger.debug("Deleting track " + myTracks[1].trackId);
          context.jamClient.TrackSetCount(1);
          //sessionModel.deleteTrack(sessionId, myTracks[1].trackId);
        }
      }

      // UNASSIGNED OUTPUTS
      $('#audio-output-unused > option').each(function () {
        logger.debug("Marking " + this.value + " as unassigned output.");
        context.jamClient.TrackSetAssignment(this.value, false, ASSIGNMENT.UNASSIGNED);
      });

      // OUTPUT
      $('#audio-output-selection > option').each(function () {
        logger.debug("Saving session audio output = " + this.value);
        context.jamClient.TrackSetAssignment(this.value, false, ASSIGNMENT.OUTPUT);
      });
    }

    function saveVoiceChatSettings() {
      var voiceChatType = isChatInputSpecified();

      originalVoiceChat = voiceChatType;

      logger.debug("Calling TrackSetChatEnable with value = " + voiceChatType);
      context.jamClient.TrackSetChatEnable(voiceChatType == VOICE_CHAT.CHAT ? true : false);

      if (voiceChatType == VOICE_CHAT.CHAT) {
        // UNASSIGNED VOICE CHAT
        $('#voice-inputs-unused > option').each(function () {
          logger.debug("Marking " + this.value + " as unassigned voice input.");
          context.jamClient.TrackSetAssignment(this.value, true, ASSIGNMENT.UNASSIGNED);
        });

        // VOICE CHAT INPUT
        $("#voice-inputs-selection > option").each(function () {
          logger.debug("Saving chat input = " + this.value);
          context.jamClient.TrackSetAssignment(this.value, true, ASSIGNMENT.CHAT);
        });
      }
      // make sure any previously assigned chat devices are marked as unassigned
      else if (voiceChatType == VOICE_CHAT.NO_CHAT) {
        // chat devices that were assigned
        $.each(chatOtherAssignedList, function (index, val) {
          logger.debug("Marking " + val.id + " as unassigned voice input.");
          context.jamClient.TrackSetAssignment(val.id, true, ASSIGNMENT.UNASSIGNED);
        });
      }
    }

    function cancelSettings() {
      logger.debug("Cancel settings");

      // reset to original device ID
      context.jamClient.TrackSetMusicDevice(originalDeviceId);

      $('#voice-chat-type').val(originalVoiceChat);

      app.layout.closeDialog('configure-audio');
    }

    function validateAudioSettings(allowEmptyInput) {
      var isValid = true;
      var noTrackErrMsg = 'You can assign no more than 2 input ports to each of your tracks. Please update your settings to correct this.';
      var noInstrumentErrMsg = 'You must specify what instrument is being played for each track. Please update your settings to correct this.';
      var outputErrMsg = 'You must assign two output ports for stereo session audio to hear music. Please update your settings to correct this.';

      var errMsg;

      // if there are no inputs remaining then allow the user to switch to the Voice Chat tab
      // to remove some
      if (allowEmptyInput && $('#audio-inputs-unused > option').size() === 0) {
        return isValid;
      }
      else {
        // verify Track 1 Input and Instrument exist
        if ($('#track1-input > option').size() === 0 || $('#track1-input > option').size() > 2) {
          errMsg = noTrackErrMsg;
          isValid = false;
        }

        if (isValid) {
          if ($('#track1-instrument > option:selected').length === 0) {
            errMsg = noInstrumentErrMsg;
            isValid = false;
          }
        }

        logger.debug("validateAudioSettings:myTrackCount=" + myTrackCount);

        // if Track 2 exists, verify Input and Instrument exist
        if (isValid) {
          var track2Count = $('#track2-input > option').size();
          if (track2Count > 2) {
            errMsg = noTrackErrMsg;
            isValid = false;
          }

          // only validate instrument if second track is selected
          if (isValid && track2Count > 0 && $('#track2-instrument > option:selected').length === 0) {
            errMsg = noInstrumentErrMsg;
            isValid = false;
          }
        }

        // verify Session Audio Output exists
        if (isValid && $('#audio-output-selection > option').size() !== 2) {
          errMsg = outputErrMsg;
          isValid = false;
        }

        if (!isValid) {
          context.JK.showErrorDialog(app, errMsg, "invalid settings");
        }
      }

      return isValid;
    }

    function validateVoiceChatSettings(allowEmptyInput) {
      var isValid = true;
      var noTrackErrMsg = 'You must select a voice chat input.';
      var limitExceededErrMsg = 'You cannot select more than 1 voice chat input.';
      var errMsg;

      if (allowEmptyInput && $('#voice-inputs-unused > option').size() === 0) {
        return isValid;
      }
      else {
        var chatType = $('#voice-chat-type').val();
        if (chatType == VOICE_CHAT.CHAT) {
          var $voiceInputSelection = $('#voice-inputs-selection > option');
          var count = $voiceInputSelection.size();
          if (count === 0) {
            // this is OK; the user may have said thy want a chat with the #voice-chat-type dropdown,
            // but if they didn't select anything, then... they actually don't
            //errMsg = noTrackErrMsg;
            isValid = true;
          }
          else if (count > 1) {
            errMsg = limitExceededErrMsg;
            isValid = false;
          }
        }
        else if (chatType == VOICE_CHAT.NO_CHAT) {
          // NO VALIDATION NEEDED
        }

        if (!isValid) {
          context.JK.showErrorDialog(app, errMsg, "invalid settings");
        }
      }

      return isValid;
    }

    function _setInstructions(type) {
      var $instructions = $('#instructions', 'div[layout-id="configure-audio"]');
      if (type === 'audio') {
        var os = context.jamClient.GetOSAsString();
        $instructions.html(configure_audio_instructions[os]);
      }
      else if (type === 'voice') {
        $instructions.html(configure_voice_instructions);
      }
    }

    function isChatInputSpecified() {
      return $('#voice-inputs-selection option').size() > 0 ? VOICE_CHAT.CHAT : VOICE_CHAT.NO_CHAT;
    }

    function _init() {
      // load instrument array for populating listboxes, using client_id in instrument_map as ID
      instrument_array = context.JK.listInstruments();

      originalVoiceChat = context.jamClient.TrackGetChatEnable() ? VOICE_CHAT.CHAT : VOICE_CHAT.NO_CHAT;

      $('#voice-chat-type').val(originalVoiceChat);

      originalDeviceId = context.jamClient.TrackGetMusicDeviceID();

      context.jamClient.TrackLoadAssignments();
      initDialogData();

      var $option1 = $('#voice-chat-type > option[value="1"]');

      // remove option 1 from voice chat if none are available and not already assigned
      if (inputUnassignedList.length === 0 && chatAssignedList.length === 0 && chatOtherAssignedList.length === 0 && chatOtherUnassignedList.length === 0) {
        $option1.remove();
      }
      // add it if it doesn't exist
      else {
        if ($option1.length === 0) {
          logger.debug("Adding Option 1 back to Voice Chat dropdown.");
          $('#voice-chat-type').append('<option value="1">Use an input from the list below for chat</option>');
        }
      }
    }

    function initialize() {
      var dialogBindings = {
        'beforeShow': function () {
          return context.JK.verifyNotRecordingForTrackChange(app);
        }
      };
      app.bindDialog('configure-audio', dialogBindings);

      events();
    }

    // should be called before opening
    function refresh() {
      _init();
      myTrackCount = myTracks.length;
    }

    this.initialize = initialize;
    this.refresh = refresh;

    this.showMusicAudioPanel = showMusicAudioPanel;
    this.showVoiceChatPanel = showVoiceChatPanel;

    return this;
  };

})(window, jQuery);
