// The recording must be fed certain events, and as a simplification to consumers, it will emit state engine transition events.
// This class automatically watches for server notifications relating to recordings (start/stop), as well as backend events
// inputs:
// * startRecording: user wants to start a recording
// * stopRecording: user wants to stop recording
//
// events:
// * startingRecording: a recording has been requested, but isn't confirmed started
// * startedRecording: a recording is officially started
// * stoppingRecording: a stop to the current recording has been requested, but it isn't confirmed yet
// * stoppedRecording: a recording is not running
// *

(function(context,$) {

    "use strict";

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

    context.JK.RecordingModel = function(app, _rest, _jamClient) {
        var currentRecording = null; // the JSON response from the server for a recording
        var currentOrLastRecordingId = null;
        var currentRecordingId = null;
        var rest = _rest;
        var currentlyRecording = false;
        var startingRecording = false;
        var stoppingRecording = false;
        var waitingOnServerStop = false;
        var waitingOnClientStop = false;
        var waitingOnStopTimer = null;
        var jamClient = _jamClient;

        var sessionId = null;
        var $self = $(this);

        function isRecording (recordingId) {
            // if you specify recordingId, the check is more exact
            if(recordingId) {
                return recordingId == currentRecordingId;
            }
            else {
                // if you omit recordingId, then we'll just check if we are recording at all
                return currentlyRecording;
            }
        }

        /** called every time a session is joined, to ensure clean state */
        function reset(_sessionId) {
            currentlyRecording = false;
            waitingOnServerStop = false;
            waitingOnClientStop = false;
            if(waitingOnStopTimer != null) {
                clearTimeout(waitingOnStopTimer);
                waitingOnStopTimer = null;
            }
            currentRecording = null;
            currentRecordingId = null;
            stoppingRecording = false;
            sessionId = _sessionId
        }



        function groupTracksToClient(recording) {
            // group N tracks to the same client Id
            var groupedTracks = {};
            var recordingTracks = recording["recorded_tracks"];
            for (var i = 0; i < recordingTracks.length; i++) {
                var clientId = recordingTracks[i].client_id;

                var tracksForClient = groupedTracks[clientId];
                if (!tracksForClient) {
                    tracksForClient = [];
                    groupedTracks[clientId] = tracksForClient;
                }
                tracksForClient.push(recordingTracks[i]);
            }
            return context.JK.dkeys(groupedTracks);
        }

        function startRecording(recordVideo, recordChat, recordFramerate) {

            $self.triggerHandler('startingRecording', {});

            currentlyRecording = true;
            stoppingRecording = false;

            context.RecordingActions.startingRecording({isRecording: false})

            // 0 indicates the NoVideoRecordActive mode; so anything but that means video got recorded
            currentRecording = rest.startRecording({"music_session_id": sessionId, record_video: recordVideo != 0})
                .done(function(recording) {
                    currentRecordingId = recording.id;
                    currentOrLastRecordingId = recording.id;

                    // ask the backend to start the session.
                    var groupedTracks = groupTracksToClient(recording);
                    jamClient.StartRecording(recording["id"], groupedTracks, recordVideo, recordChat, recordFramerate);
                })
                .fail(function(jqXHR) {
                    var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
                    $self.triggerHandler('startedRecording', details);
                    currentlyRecording = false;
                    context.RecordingActions.startedRecording(details);
                })


            return true;
        }

        /** Nulls can be passed for all 3 currently; that's a user request.   */
        function stopRecording(recordingId, reason, detail) {

            if(stoppingRecording) {
              logger.debug("ignoring stopRecording because we are already stopping");
              return;
            }
            stoppingRecording = true;


            waitingOnServerStop = waitingOnClientStop = true;
            waitingOnStopTimer = setTimeout(timeoutTransitionToStop, 5000);

            $self.triggerHandler('stoppingRecording', {reason: reason, detail: detail});
            context.RecordingActions.stoppingRecording({reason: reason, detail: detail, isRecording:true})

            // this path assumes that the currentRecording info has, or can be, retrieved
            // failure for currentRecording is handled elsewhere
            currentRecording
                .done(function(recording) {

                var groupedTracks = groupTracksToClient(recording);

                //if(sessionModel.jamTracks() && isRecording()) {
                //  logger.debug("preemptive stop media")
                  //context.jamClient.SessionStopPlay();
                //}

                logger.debug("stopping recording")
                jamClient.StopRecording(recording.id, groupedTracks);

                rest.stopRecording( { "id": recording.id } )
                    .done(function() {
                        waitingOnServerStop = false;
                        attemptTransitionToStop(recording.id, reason, detail);
                    })
                    .fail(function(jqXHR) {
                        if(jqXHR.status == 422) {
                            waitingOnServerStop = false;
                            attemptTransitionToStop(recording.id, reason, detail);
                        }
                        else {
                            logger.error("unable to stop recording %o", arguments);
                            transitionToStopped();
                            var details = {'recordingId': recording.id, 'reason' : 'rest', 'details' : arguments, isRecording: false}
                            $self.triggerHandler('stoppedRecording', details);
                            context.RecordingActions.stoppedRecording(details)
                        }
                    });
            });
            return true;
        }

        function abortRecording(recordingId, errorReason, errorDetail) {
            jamClient.AbortRecording(recordingId, {reason: errorReason, detail: errorDetail, success:false});
        }

        function timeoutTransitionToStop() {
            // doh.  couldn't stop
            waitingOnStopTimer = null;
            transitionToStopped();
            $self.triggerHandler('stoppedRecordingFailed', { 'reason' : 'timeout' });
        }

        // Only tell the user that we've stopped once both server and client agree we've stopped
        function attemptTransitionToStop(recordingId, errorReason, errorDetail) {

            if(!waitingOnClientStop && !waitingOnServerStop) {
                transitionToStopped();
                var details = {recordingId: recordingId, reason: errorReason, detail: errorDetail, isRecording: false}
                $self.triggerHandler('stoppedRecording', details)
                context.RecordingActions.stoppedRecording(details)
            }
        }

        function transitionToStopped() {
            currentlyRecording = false;
            currentRecording = null;
            currentRecordingId = null;
            if(waitingOnStopTimer) {
                clearTimeout(waitingOnStopTimer);
                waitingOnStopTimer = null;
            }
        }

        function onServerStartRecording() {

        }

        function onServerStopRecording(recordingId) {
            stopRecording(recordingId, null, null);
        }

        function handleRecordingStartResult(recordingId, result) {

            var success = result.success;
            var reason = result.reason;
            var detail = result.detail;


            if(success) {
                var details = {clientId: app.clientId, isRecording:true}
                $self.triggerHandler('startedRecording', details)
                context.RecordingActions.startedRecording(details)
            }
            else {
                currentlyRecording = false;
                logger.error("unable to start the recording %o, %o", reason, detail);
                var details = { clientId: app.clientId, reason: reason, detail: detail, isRecording: false}
                $self.triggerHandler('startedRecording', details);
                context.RecordingActions.startedRecording(details)
            }
        }

        function handleRecordingStopResult(recordingId, result) {

            var success = result.success;
            var reason = result.reason;
            var detail = result.detail;

            waitingOnClientStop = false;

            if(success) {
                attemptTransitionToStop(recordingId, reason, detail);
            }
            else {
                transitionToStopped();
                logger.error("backend unable to stop the recording %o, %o", reason, detail);
                var details = {recordingId: recordingId, reason: reason, detail : detail, isRecording: false}
                $self.triggerHandler('stoppedRecording', details);
                context.RecordingActions.stoppedRecording(details)
            }
        }

        function handleRecordingStarted(recordingId, result, clientId) {
            var success = result.success;
            var reason = result.reason;
            var detail = result.detail;

            // in this scenario, we don't know all the tracks of the user.
            // we need to ask sessionModel to populate us with the recording data ASAP

            currentRecording = rest.getRecording({id: recordingId})
                .fail(function() {
                    abortRecording(recordingId, 'populate-recording-info', app.clientId);
                })
                .done(function(recording) {
                    currentRecordingId = recording.id;
                    currentOrLastRecordingId = recording.id;
                });

            var details = {recordingId: recordingId, isRecording: false}
            $self.triggerHandler('startingRecording', details);
            context.RecordingActions.startingRecording(details)
            currentlyRecording = true;

            details = {clientId: clientId, recordingId: recordingId, isRecording: true}
            $self.triggerHandler('startedRecording', details);
            context.RecordingActions.startedRecording(details)
        }

        function handleRecordingStopped(recordingId, result) {

            if(recordingId == "video") {
                // comes from VideoRecordingStopped
                return;
            }

            logger.debug("handleRecordingStopped " + recordingId, result)

            var success = result.success;
            var reason = result.reason;
            var detail = result.detail;


            var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: true }
            $self.triggerHandler('stoppingRecording', details);
            context.RecordingActions.stoppingRecording(details)

            // the backend says the recording must be stopped.
            // tell the server to stop it too
            rest.stopRecording({
                id: recordingId
            })
                .always(function() {
                    transitionToStopped();
                })
                .fail(function(jqXHR, textStatus, errorMessage) {
                    if(jqXHR.status == 422) {
                        logger.debug("recording already stopped %o", arguments);
                        var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
                        $self.triggerHandler('stoppedRecording', details);
                        context.RecordingActions.stoppedRecording(details)
                    }
                    else if(jqXHR.status == 404) {
                        logger.debug("recording is already deleted %o", arguments);
                        var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
                        $self.triggerHandler('stoppedRecording', details);
                        context.RecordingActions.stoppedRecording(details)
                    }
                    else {
                        var details = {recordingId: recordingId, reason: textStatus, detail: errorMessage, isRecording: false}
                        $self.triggerHandler('stoppedRecording', details);
                        context.RecordingActions.stoppedRecording(details)
                    }
                })
                .done(function() {
                    var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
                    $self.triggerHandler('stoppedRecording', details);
                    context.RecordingActions.stoppedRecording(details)
                })
        }

        function handleRecordingAborted(recordingId, result) {

            if(recordingId == "video") {

                // comes from AbortedVideoRecording
                recordingId = result;
                if (arguments.length == 2) {
                    result =  arguments[2]
                }
                logger.debug("video recording aborted", result)
                context.JK.Banner.showAlert("Video has stopped recording.  Audio is still recording.")
                //context.RecordingActions.stopRecording()

                return;
            }

            var success = result.success;
            var reason = result.reason;
            var detail = result.detail;

            stoppingRecording = false;

            var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false }
            $self.triggerHandler('abortedRecording', details);
            context.RecordingActions.abortedRecording(details)
            // the backend says the recording must be stopped.
            // tell the server to stop it too
            rest.stopRecording({
                id: recordingId
            })
            .always(function() {
                currentlyRecording = false;
            })
        }

        /**
         * If a stop is needed, it will be issued, and the deferred object will fire done()
         * If a stop is not needed (i.e., there is no recording), then the deferred object will fire immediately
         * @returns {$.Deferred} in all cases, only .done() is fired.
         */
        function stopRecordingIfNeeded() {
            var deferred = new $.Deferred();

            function resolved() {
               $self.off('stoppedRecording.stopRecordingIfNeeded', resolved);
               deferred.resolve(arguments);
            }

            if(!currentlyRecording) {
                deferred = new $.Deferred();
                deferred.resolve();
            }
            else {
                // wait for the next stoppedRecording event message
                $self.on('stoppedRecording.stopRecordingIfNeeded', resolved);

                if(!stopRecording()) {
                    // no event is coming, so satisfy the deferred immediately
                    $self.off('stoppedRecording.stopRecordingIfNeeded', resolved);
                    deferred = new $.Deferred();
                    deferred.resolve();
                }
            }

            return deferred;

        }

        this.initialize = function() {
        };

        this.startRecording  = startRecording;
        this.stopRecording = stopRecording;
        this.onServerStopRecording = onServerStopRecording;
        this.isRecording = isRecording;
        this.reset = reset;
        this.stopRecordingIfNeeded = stopRecordingIfNeeded;
        this.currentOrLastRecordingId = function () { return currentOrLastRecordingId; };

        context.JK.HandleRecordingStartResult  = handleRecordingStartResult;
        context.JK.HandleRecordingStopResult = handleRecordingStopResult;
        context.JK.HandleRecordingStopped = handleRecordingStopped;
        context.JK.HandleRecordingStarted = handleRecordingStarted;
        context.JK.HandleRecordingAborted = handleRecordingAborted;


    };

 })(window,jQuery);