/**
 * Functions related to faders (slider controls for volume).
 * These functions are intimately tied to the markup defined in
 * the templates in _faders.html.erb
 */
(function (g, $) {

  "use strict";

  g.JK = g.JK || {};

  var $draggingFaderHandle = null;
  var $draggingFader = null;
  var $floater = null;
  var draggingOrientation = null;

  var logger = g.JK.logger;

  function faderClick(e) {

    e.stopPropagation();

    var $fader = $(this);
    var floaterConvert = $fader.data('floaterConverter')
    var sessionModel = window.JK.CurrentSessionModel || null;

    /**
    if(!$fader.data('has-session-control')) {
      var sessionControllerName = $fader.data('session-controller-name');
      window.JK.prodBubble($fader, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $fader.closest('.top-parent'), duration:12000})
      return false;
    }*/

    var mediaControlsDisabled = $fader.data('media-controls-disabled');
    if(mediaControlsDisabled) {
      var mediaTrackOpener = $fader.data('media-track-opener');
      window.JK.prodBubble($fader, 'media-controls-disabled', {mediaTrackOpener:mediaTrackOpener}, {positions:['top'], offsetParent: $fader.closest('.screen')})
      return false;
    }

    if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $control.closest('.session-track').data('track_data').type == 'jam_track') {
      window.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $fader.closest('.screen')})
      return false;
    }
    
    if($fader.data('showHelpAboutMediaMixers')) {
      if(sessionModel) {
        if(!sessionModel.hasShownAudioMediaMixerHelp()) {
          window.JK.prodBubble($fader, 'volume-media-mixers', {}, {positions:['top'], offsetParent: $fader.closest('.screen')})
          sessionModel.markShownAudioMediaMixerHelp()
        }
      }
    }

    draggingOrientation = $fader.attr('data-orientation');
    var offset = $fader.offset();
    var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}

    var faderPct = faderValue($fader, e, position);

    if (faderPct < 0 || faderPct > 100) {
      return false;
    }

    if(floaterConvert) {
      window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct))
    }

    $fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})

    setHandlePosition($fader, faderPct);
    return false;
  }

  function setHandlePosition($fader, value) {
    var ratio, position;
    var $handle = $fader.find('div[data-control="fader-handle"]');

    var orientation = $fader.attr('data-orientation');
    var handleCssAttribute = getHandleCssAttribute($fader);

    // required because this method is entered directly when from a callback

    if (draggingOrientation === "horizontal") {
      ratio = value / 100;
      position = ((ratio * $fader.width()) - (ratio * handleWidth(draggingOrientation))) + 'px';
    }
    else {
      ratio = (100 - value) / 100;
      position = ((ratio * $fader.height()) - (ratio * handleWidth(draggingOrientation))) + 'px';
    }

    $handle.css(handleCssAttribute, position);
  }

  function faderValue($fader, e, offset) {
    var orientation = $fader.attr('data-orientation');
    var getPercentFunction = getVerticalFaderPercent;
    var relativePosition = offset.top;
    if (orientation && orientation == 'horizontal') {
      getPercentFunction = getHorizontalFaderPercent;
      relativePosition = offset.left;
    }
    return getPercentFunction(relativePosition, $fader);
  }

  function getHandleCssAttribute($fader) {
    var orientation = $fader.attr('data-orientation');
    return (orientation === 'horizontal') ? 'left' : 'top';
  }

  function getVerticalFaderPercent(eventY, $fader) {
    return getFaderPercent(eventY, $fader, 'vertical');
  }

  function getHorizontalFaderPercent(eventX, $fader) {
    return getFaderPercent(eventX, $fader, 'horizontal');
  }

  /**
   * Returns the current value of the fader as int percent 0-100
   */
  function getFaderPercent(value, $fader, orientation) {
    var faderSize, faderPct;

    // the handle takes up room, and all calculations use top.  So when the
    // handle *looks* like it's at the bottom by the user, it won't give a 0% value.
    // so, we subtract handleWidth from the size of it's parent

    if (orientation === "horizontal") {
      faderSize = $fader.width();
      faderPct = Math.round(( value + (value / faderSize * handleWidth(orientation))) / faderSize * 100);
    }
    else {
      faderSize = $fader.height();
      faderPct = Math.round((faderSize - handleWidth(orientation) - value) / (faderSize - handleWidth(orientation)) * 100);
    }

    return faderPct;
  }

  function onFaderDrag(e, ui) {
    var faderPct = faderValue($draggingFader, e, ui.position);

    // protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
    if (faderPct < 0 || faderPct > 100) {
      return false;
    }

    // simple snap feature to stick to the mid point
    if(faderPct > 46 && faderPct < 54 && $draggingFader.data('snap')) {
      faderPct = 50
      var orientation = $draggingFader.attr('data-orientation');
      if(orientation == 'horizontal') {
        var width = $draggingFader.width()
        var left = width / 2
        ui.position.left = left
      }
      else {
        var height = $draggingFader.height()
        var top = height / 2
        ui.position.top = top
      }
    }

    var floaterConvert = $draggingFaderHandle.data('floaterConverter')

    if(floaterConvert && $floater) {
      window.JK.FaderHelpers.setFloaterValue($floater, floaterConvert(faderPct))
    }
    $draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true})
  }

  function onFaderDragStart(e, ui) {
    $draggingFaderHandle = $(this);
    $draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]');
    $floater = $draggingFaderHandle.find('.floater')
    draggingOrientation = $draggingFader.attr('orientation');

    var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
    var mediaTrackOpener = $draggingFaderHandle.data('media-track-opener');
    var sessionModel = window.JK.CurrentSessionModel || null;

    /**
    if(!$draggingFaderHandle.data('has-session-control')) {
      var sessionControllerName = $draggingFaderHandle.data('session-controller-name');
      window.JK.prodBubble($draggingFaderHandle, 'not-session-controller', {sessionControllerName:sessionControllerName}, {positions:['left', 'right'], offsetParent: $draggingFaderHandle.closest('.top-parent'), duration:12000})
      return false;
    }*/

    if(mediaControlsDisabled) {
      return false;
    }

    if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $draggingFaderHandle.closest('.session-track').data('track_data').type == 'jam_track') {
      return false;
    }
    
    return true;
  }

  function onFaderDragStop(e, ui) {

    if($draggingFader.data('showHelpAboutMediaMixers')) {
      if(window.JK.CurrentSessionModel) {
        if(!window.JK.CurrentSessionModel.hasShownAudioMediaMixerHelp()) {
          window.JK.prodBubble($draggingFader, 'volume-media-mixers', {}, {positions:['bottom'], offsetParent: $draggingFader.closest('.screen')})
          window.JK.CurrentSessionModel.markShownAudioMediaMixerHelp()
        }
      }
    }

    var faderPct = faderValue($draggingFader, e, ui.position);

    // protect against attempts to drag outside of the slider, which jquery.draggable sometimes allows
    // do not return 'false' though, because that stops future drags from working, for some reason
    if (faderPct < 0 || faderPct > 100) {
      return;
    }

    $draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false});

    $draggingFaderHandle = null;
    $draggingFader = null;
    draggingOrientation = null;
  }

  function handleWidth(orientation) {
    return orientation === "horizontal" ? 8 : 11;
  }

  g.JK.FaderHelpers = {

    /**
     * Render a fader into the element identifed by the provided
     * selector, with the provided options.
     */
    renderFader: function (selector, userOptions) {
      selector = $(selector);
      if (userOptions === undefined) {
        throw ("renderFader: userOptions is required");
      }
      var renderDefaults = {
        faderType: "vertical",
        height: 83,  // only used for vertical
        width: 83    // only used for horizontal
      };
      var options = $.extend({}, renderDefaults, userOptions);
      var templateSelector = (options.faderType === 'horizontal') ?
        "#template-fader-h" : '#template-fader-v';
      var templateSource = $(templateSelector).html();

      selector.html(g._.template(templateSource, options));

      selector.find('div[data-control="fader"]')
        .data('media-controls-disabled', selector.data('media-controls-disabled'))
        .data('media-track-opener', selector.data('media-track-opener'))
        .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))

      selector.find('div[data-control="fader-handle"]').draggable({
        drag: onFaderDrag,
        start: onFaderDragStart,
        stop: onFaderDragStop,
        containment: "parent",
        axis: options.faderType === 'horizontal' ? 'x' : 'y'
      }).data('media-controls-disabled', selector.data('media-controls-disabled'))
        .data('media-track-opener', selector.data('media-track-opener'))
        .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))

      // Embed any custom styles, applied to the .fader below selector
      if ("style" in options) {
        for (var key in options.style) {
          selector.find(' .fader').css(key, options.style[key]);
        }
      }
    },

    renderFader2: function (selector, userOptions, floaterConverter) {
      selector = $(selector);
      if (userOptions === undefined) {
        throw ("renderFader: userOptions is required");
      }
      var renderDefaults = {
        faderType: "vertical",
        sessionController: null
      };
      var options = $.extend({}, renderDefaults, userOptions);

      var sessionCanControl = true
      var sessionControllerName = null
      if(userOptions.sessionController) {
        if(!userOptions.sessionController.can_control) {
          sessionCanControl = false
          sessionControllerName = userOptions.sessionController.session_controller.name
        }
      }

      selector.find('div[data-control="fader"]')
        .data('media-controls-disabled', selector.data('media-controls-disabled'))
        .data('media-track-opener', selector.data('media-track-opener'))
        .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
        .data('floaterConverter', floaterConverter)
        .data('snap', userOptions.snap)
        .data('has-session-control', sessionCanControl)
        .data('session-controller-name', sessionControllerName)


      if(userOptions.sessionController) {

      }

      selector.find('div[data-control="fader-handle"]').draggable({
        drag: onFaderDrag,
        start: onFaderDragStart,
        stop: onFaderDragStop,
        containment: "parent",
        axis: options.faderType === 'horizontal' ? 'x' : 'y'
      }).data('media-controls-disabled', selector.data('media-controls-disabled'))
        .data('media-track-opener', selector.data('media-track-opener'))
        .data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
        .data('floaterConverter', floaterConverter)
        .data('snap', userOptions.snap)
        .data('has-session-control', sessionCanControl)
        .data('session-controller-name', sessionControllerName)

      // Embed any custom styles, applied to the .fader below selector
      if ("style" in options) {
        for (var key in options.style) {
          selector.find(' .fader').css(key, options.style[key]);
        }
      }
    },

    convertLinearToDb: function (input) {

      // deal with extremes better
      if (input <= 1) { return -80; }
      if (input >= 99) { return 20; }

      var temp;
      temp = 0.0;
      // coefficients
      var a = -8.0013990435159329E+01;
      var b = 4.1755639785242042E+00;
      var c = -1.4036729740086906E-01;
      var d = 3.1788545454166156E-03;
      var f = -3.5148685730880861E-05;
      var g = 1.4221429222004657E-07;
      temp += a +
        b * input +
        c * Math.pow(input, 2.0) +
        d * Math.pow(input, 3.0) +
        f * Math.pow(input, 4.0) +
        g * Math.pow(input, 5.0);

      return temp;
    },


    convertPercentToAudioTaper: function (input) {

      // composite function resembling audio taper
      if (input <= 1)  { return -80; }
      if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db
      if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db
      if (input < 99)  { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db
      if (input >= 99) { return 20; }

    },

    convertAudioTaperToPercent: function(db) {
      if(db <= -78) { return 0}
      if(db <= -24) { return (db + 80) / 2 }
      if(db <= 1.5) { return (db + 38) / .5 }
      if(db <= 19.125) { return (db + 67.5) / 0.875 }
      return 100;
    },


    setFaderValue: function (faderId, faderValue, floaterValue) {
      var $fader = $('[data-fader-id="' + faderId + '"]');
      this.setHandlePosition($fader, faderValue);
      if(floaterValue !== undefined) {
        var $floater = $fader.find('.floater')
        this.setFloaterValue($floater, floaterValue)
      }
    },

    showFader: function(faderId) {
      var $fader = $('[data-fader-id="' + faderId + '"]');
      $fader.find('div[data-control="fader-handle"]').show()
    },

    setHandlePosition: function ($fader, faderValue) {
      draggingOrientation = $fader.attr('data-orientation');
      setHandlePosition($fader, faderValue);
      draggingOrientation = null;
    },

    setFloaterValue: function($floater, floaterValue) {
      $floater.text(floaterValue)
    },

    initialize: function () {
      $('body').on('click', 'div[data-control="fader"]', faderClick);
    }

  };


})(window, jQuery);