context = window
logger = context.JK.logger
SessionActions = context.SessionActions
JamTrackActions = context.JamTrackActions
MIX_MODES = context.JK.MIX_MODES
rest = context.JK.Rest()

@MixerStore = Reflux.createStore(
  {
    METRO_SOUND_LOOKUP:  {
      0 : "BuiltIn",
      1 : "SineWave",
      2 : "Beep",
      3 : "Click",
      4 : "Kick",
      5 : "Snare",
      6 : "MetroFile"
    }

    metro: {tempo: 120, cricket: false, sound: "Beep" }
    noAudioUsers : {}
    checkingMissingPeers : {}
    missingMixerPeers : {}
    recheckTimeout : null
    clientsWithAudioOverride : {}

    init: ->
      # Register with the app store to get @app
      this.listenTo(context.AppStore, this.onAppInit);
      this.listenTo(context.SessionStore, this.onSessionChange)
      this.listenTo(context.MixerActions.mute, this.onMute)
      this.listenTo(context.MixerActions.faderChanged, this.onFaderChanged)
      this.listenTo(context.MixerActions.initGain, this.onInitGain)
      this.listenTo(context.MixerActions.initPan, this.onInitPan)
      this.listenTo(context.MixerActions.panChanged, this.onPanChanged)
      this.listenTo(context.MixerActions.mixersChanged, this.onMixersChanged)
      this.listenTo(context.MixerActions.syncTracks, this.onSyncTracks)
      this.listenTo(context.MixerActions.mixerModeChanged, this.onMixerModeChanged)
      this.listenTo(context.MixerActions.loopChanged, this.onLoopChanged)
      this.listenTo(context.MixerActions.openMetronome, this.onOpenMetronome)
      this.listenTo(context.MixerActions.metronomeChanged, this.onMetronomeChanged)
      this.listenTo(context.MixerActions.deadUserRemove, this.onDeadUserRemove)
      this.listenTo(context.MixerActions.missingPeerMixer, this.onMissingPeerMixer)
      this.listenTo(context.MixerActions.clientsWithAudio, this.onClientsWithAudio)
      this.listenTo(context.MixerActions.closeMedia, this.onCloseMedia)

      context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback
      context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback
      context.JK.HandleBridgeCallback2 = @handleBridgeCallback
      context.JK.HandleBackingTrackSelectedCallback2 = @handleBackingTrackSelectedCallback

      #setInterval(@dumpVUStats, 5000)

    dumpVUStats: () ->
      @mixers.dumpVUStats() if @mixers?

    issueChange: () ->
      @trigger({session: @session, mixers: @mixers})

    handleVolumeChangeCallback: (mixerId, isLeft, value, isMuted) ->
      # TODO
      # Visually update mixer
      # There is no need to actually set the back-end mixer value as the
      # back-end will already have updated the audio mixer directly prior to sending
      # me this event. I simply need to visually show the new fader position.
      # TODO: Use mixer's range
      #faderValue = percentFromMixerValue(-80, 20, value);
      #context.JK.FaderHelpers.setFaderValue(mixerId, faderValue);
      #var $muteControl = $('[control="mute"][mixer-id="' + mixerId + '"]');
      #_toggleVisualMuteControl($muteControl, isMuted);
      logger.debug("volume change")


    handleMetronomeCallback: (args) ->
      logger.debug("MetronomeCallback: ", args)
      @metro.tempo = args.bpm
      @metro.cricket = args.cricket;
      @metro.sound = @METRO_SOUND_LOOKUP[args.sound];

      # This isn't actually there, so we rely on the metroSound as set from select on form:
      # metroSound = args.sound
      SessionActions.syncWithServer()

      @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)

      @issueChange()

    handleBridgeCallback: (vuData) ->

      eventName = null
      mixerId = null
      value = null
      vuInfo = null

      for vuInfo in vuData
        eventName = vuInfo[0];
        vuVal = 0.0;
        if eventName == "vu"
          mixerId         = vuInfo[1];
          mode            = vuInfo[2];
          leftValue       = vuInfo[3];
          leftClipping    = vuInfo[4];
          rightValue      = vuInfo[5];
          rightClipping   = vuInfo[6];
          # TODO - no guarantee range will be -80 to 20. Get from the
          # GetControlState for this mixer which returns min/max
          # value is a DB value from -80 to 20. Convert to float from 0.0-1.0

          @mixers.updateVU(mixerId, mode, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping)
          #@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)


    handleBackingTrackSelectedCallback: () ->
      logger.debug("backing track selected")

    onAppInit: (@app) ->
      @gearUtils = context.JK.GearUtilsInstance
      @sessionUtils = context.JK.SessionUtils

      context.jamClient.SetVURefreshRate(150)
      context.jamClient.RegisterVolChangeCallBack("JK.HandleVolumeChangeCallback2")
      context.jamClient.setMetronomeOpenCallback("JK.HandleMetronomeCallback2")


    sessionEnded: () ->
      @checkingMissingPeers = {}
      @noAudioUsers = {}
      @missingMixerPeers = {}
      clearTimeout(@recheckTimeout) if @recheckTimeout?

    onSessionChange: (session) ->

      @sessionEnded() unless session.inSession()

      @session = session

      @masterMixers = context.jamClient.SessionGetAllControlState(true);
      @personalMixers = context.jamClient.SessionGetAllControlState(false);

      @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)

      @issueChange()

    onMute: (mixers, muting) ->

      mixers = [mixers] unless $.isArray(mixers)

      for mixer in mixers
        @mixers.mute(mixer.id, mixer.mode, muting);

      # simulate a state change to cause a UI redraw
      @issueChange()

    onFaderChanged: (data, mixers, gainType, controlGroup) ->

      @mixers.faderChanged(data, mixers, gainType, controlGroup)

      @issueChange()

    onPanChanged: (data, mixers, groupId) ->
      @mixers.panChanged(data, mixers, groupId)

      @issueChange()

    onLoopChanged: (mixer, shouldLoop) ->
      @mixers.loopChanged(mixer, shouldLoop)

    onOpenMetronome: () ->
      context.jamClient.SessionStopPlay()
      context.jamClient.SessionOpenMetronome(@mixers.metro.tempo, @mixers.metro.sound, 1, 0)

    onMetronomeChanged: (tempo, sound) ->
      logger.debug("onMetronomeChanged", tempo, sound)

      @metro.tempo = tempo
      @metro.sound = sound
      context.jamClient.SessionSetMetronome(@metro.tempo, @metro.sound, 1, 0);

      @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
      @issueChange()

  # codeInitiated means the user did not initiate this
    onCloseMedia: (codeInitiated) ->
      logger.debug("MixerStore: onCloseMedia", codeInitiated)
      if @session.recordedTracks()
        @closeRecording(codeInitiated)
      else if @session.jamTracks() || @session.downloadingJamTrack
        @closeJamTrack(codeInitiated)
      else if @session.backingTrack() && @session.backingTrack().path
        @closeBackingTrack(codeInitiated)
      else if @session.isMetronomeOpen()
        @closeMetronomeTrack(codeInitiated)
      else
        logger.error("don't know how to close open media", @session) unless codeInitiated

    closeRecording: (codeInitiated) ->
      logger.debug("closing recording");

      if !@mixers.mediaSummary.isOpener
        logger.debug("not recordingas not opener")
        return

      SessionActions.closeMedia(codeInitiated)

    closeMetronomeTrack:(codeInitiated) ->
      logger.debug("closing metronome")

      SessionActions.closeMedia(codeInitiated)

    closeBackingTrack: (codeInitiated) ->
      logger.debug("closing backing track");

      if !@mixers.mediaSummary.isOpener
        logger.debug("not closing backing as not opener")
        return

      SessionActions.closeMedia(codeInitiated)

    closeJamTrack: (codeInitiated) ->
      logger.debug("closing jam track");

      if !@mixers.mediaSummary.isOpener
        logger.debug("not closing jamtrack as not opener")
        return

      SessionActions.closeMedia(codeInitiated)


    onDeadUserRemove: (clientId) ->
      return unless @session.inSession()

      participant = @session.participantsEverSeen[clientId];

      if participant?
        logger.debug("todo :notify dead user")
        # XXX TODO trigger some notification store

        #app.notify({
        #  "title": ALERT_TYPES[type].title,
        #  "text": participant.user.name + " is no longer sending audio.",
        #  "icon_url": context.JK.resolveAvatarUrl(participant.user.photo_url)
        #});

      @noAudioUsers[clientId] = true

      @issueChange()

    onClientsWithAudio: (clients) ->
      @clientsWithAudioOverride = clients

    onMissingPeerMixer: (clientId) ->

      missingPeerAttempts = @missingMixerPeers[clientId]

      # check for 5 tries (10 seconds if setTimeout is 2000ms)
      if !missingPeerAttempts? || missingPeerAttempts < 5
        @checkingMissingPeers[clientId] = true
        @recheckTimeout = setTimeout(@recheckForMixers, 2000) unless @recheckTimeout?
      else
        logger.debug("ignoring missing peer recheck. missingPeerAttempts: #{missingPeerAttempts}")

    recheckForMixers: () ->
      # increment how many times we've checked for this particular peer
      for clientId, meh of @checkingMissingPeers
        missingPeerAttempts = @missingMixerPeers[clientId]
        missingPeerAttempts = 0 unless missingPeerAttempts?
        missingPeerAttempts++
        @missingMixerPeers[clientId] = missingPeerAttempts

      # reset the peers we are looking for
      @checkingMissingPeers = {}

      @recheckTimeout = null
      @masterMixers = context.jamClient.SessionGetAllControlState(true);
      @personalMixers = context.jamClient.SessionGetAllControlState(false);
      logger.debug("MixerStore: recheckForMixers")
      @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)
      @issueChange()

    onInitGain: (mixer) ->
      @mixers.initGain(mixer)

    onInitPan: (mixer) ->
      @mixers.initPan(mixer)

    onMixersChanged: (type, text) ->
      @masterMixers = context.jamClient.SessionGetAllControlState(true);
      @personalMixers = context.jamClient.SessionGetAllControlState(false);

      logger.debug("MixerStore: onMixersChanged")

      @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL)

      SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())

      @issueChange()

    onMixerModeChanged: (mode) ->
      if mode == MIX_MODES.MASTER
        @app.layout.showDialog('session-master-mix-dialog') unless @app.layout.isDialogShowing('session-master-mix-dialog')
      else
        @app.layout.closeDialog('session-master-mix-dialog') if @app.layout.isDialogShowing('session-master-mix-dialog')

    onSyncTracks: () ->
      logger.debug("MixerStore: onSyncTracks")
      unless @session.inSession()
        logger.debug("dropping sync tracks because no longer in session")
        return

      allTracks = @mixers.getTrackInfo()

      inputTracks = allTracks.userTracks;
      backingTracks = allTracks.backingTracks;
      metronomeTracks = allTracks.metronomeTracks;

      # create a trackSync request based on backend data
      syncTrackRequest = {}
      syncTrackRequest.client_id = @app.clientId
      syncTrackRequest.tracks = inputTracks
      syncTrackRequest.backing_tracks = backingTracks
      syncTrackRequest.metronome_open = metronomeTracks.length > 0
      syncTrackRequest.id = @session.id()

      rest.putTrackSyncChange(syncTrackRequest)
      .fail((jqXHR)=>
        if jqXHR.status != 404
          @app.notify({
            "title": "Can't Sync Local Tracks",
            "text": "The client is unable to sync local track information with the server.  You should rejoin the session to ensure a good experience.",
            "icon_url": "/assets/content/icon_alert_big.png"
          })

        else
          logger.debug("Unable to sync local tracks because session is gone.")
      )
  }
)
