// this code simulates what the actual backend recording feature will do
(function(context, $) {


    "use strict";

    context.JK = context.JK || {};
    context.JK.FakeJamClientRecordings = function(app, fakeJamClient, p2pMessageFactory) {

        var logger = context.JK.logger;

        var startRecordingResultCallbackName = null;
        var stopRecordingResultCallbackName = null;
        var startedRecordingResultCallbackName = null;
        var stoppedRecordingEventCallbackName = null;
        var abortedRecordingEventCallbackName = null;

        var startingSessionState = null;
        var stoppingSessionState = null;

        var currentRecordingId = null;
        var currentRecordingCreatorClientId = null;
        var currentRecordingClientIds = null;

        function timeoutStartRecordingTimer() {
            eval(startRecordingResultCallbackName).call(this, startingSessionState.recordingId, {success:false, reason:'client-no-response', detail:startingSessionState.groupedClientTracks[0]});
            startingSessionState = null;
        }

        function timeoutStopRecordingTimer() {
             eval(stopRecordingResultCallbackName).call(this, stoppingSessionState.recordingId, {success:false, reason:'client-no-response', detail:stoppingSessionState.groupedClientTracks[0]});
        }

        function StartRecording(recordingId, clients) {
            startingSessionState = {};

            // we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
            startingSessionState.aggegratingStartResultsTimer = setTimeout(timeoutStartRecordingTimer, 1000);
            startingSessionState.recordingId = recordingId;
            startingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId); // we will manipulate this new one

            // store the current recording's data
            currentRecordingId = recordingId;
            currentRecordingCreatorClientId = app.clientId;
            currentRecordingClientIds = copyClientIds(clients, app.clientId);

            if(startingSessionState.groupedClientTracks.length == 0) {
                // if there are no clients but 'self', then you can declare a successful recording immediately
                finishSuccessfulStart(recordingId);
            }
            else {
                // signal all other connected clients that the recording has started
                for(var i = 0; i < startingSessionState.groupedClientTracks.length; i++) {
                    var clientId = startingSessionState.groupedClientTracks[i];
                    context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.startRecording(recordingId)));
                }
            }
        }

        function StopRecording(recordingId, clients, result) {

            if(startingSessionState) {
                // we are currently starting a session.
                // TODO
            }

            if(!result) {
                result = {success:true}
            }

            stoppingSessionState = {};

            // we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
            stoppingSessionState.aggegratingStopResultsTimer = setTimeout(timeoutStopRecordingTimer, 1000);
            stoppingSessionState.recordingId = recordingId;
            stoppingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId);

            if(stoppingSessionState.groupedClientTracks.length == 0) {
                finishSuccessfulStop(recordingId);
            }
            else {
                // signal all other connected clients that the recording has stopped
                for(var i = 0; i < stoppingSessionState.groupedClientTracks.length; i++) {
                    var clientId = stoppingSessionState.groupedClientTracks[i];
                    context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.stopRecording(recordingId, result.success, result.reason, result.detail)));
                }
            }
        }

        function AbortRecording(recordingId, errorReason, errorDetail) {
            // todo check recordingId
            context.JK.JamServer.sendP2PMessage(currentRecordingCreatorClientId, JSON.stringify(p2pMessageFactory.abortRecording(recordingId, errorReason, errorDetail)));
        }

        function onStartRecording(from, payload) {
            logger.debug("received start recording request from " + from, context.SessionStore.isRecording);
            if(context.SessionStore.isRecording) {
                // reject the request to start the recording
                context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, false, "already-recording", null)));
            }
            else {
                // accept the request, and then tell the frontend we are now recording
                // a better client implementation would verify that the tracks specified match that what we have configured currently

                // store the current recording's data
                currentRecordingId = payload.recordingId;
                currentRecordingCreatorClientId = from;

                context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, true, null, null)));
                eval(startedRecordingResultCallbackName).call(this, payload.recordingId, {success:true}, from);
            }
        }

        function onStartRecordingAck(from, payload) {
            logger.debug("received start recording ack from " + from);

            // we should check transactionId; this could be an ACK for a different recording
            if(startingSessionState) {

                if(payload.success) {
                    var index = startingSessionState.groupedClientTracks.indexOf(from);
                    startingSessionState.groupedClientTracks.splice(index, 1);

                    if(startingSessionState.groupedClientTracks.length == 0) {
                        finishSuccessfulStart(payload.recordingId);
                    }
                }
                else {
                    // TOOD: a client responded with error; we need to tell all other clients to abandon recording
                    logger.warn("received an unsuccessful start_record_ack from: " + from);
                }
            }
            else {
                logger.warn("received a start_record_ack when there is no recording starting from: " + from);
                // TODO: this is an error case; we should signal back to the sender that we gave up
            }
        }

        function onStopRecording(from, payload) {
          logger.debug("received stop recording request from " + from);

          // TODO check recordingId, and if currently recording
          // we should return success if we are currently recording, or if we were already asked to stop for this recordingId
          // this means we should keep a list of the last N recordings that we've seen, rather than just keeping the current
          context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.stopRecordingAck(payload.recordingId, true)));

          if(stopRecordingResultCallbackName) {
            eval(stopRecordingResultCallbackName).call(this, payload.recordingId, {success:payload.success, reason:payload.reason, detail:from});
          }
        }

        function onStopRecordingAck(from, payload) {
            logger.debug("received stop recording ack from " + from);

            // we should check transactionId; this could be an ACK for a different recording
            if(stoppingSessionState) {

                if(payload.success) {
                    var index = stoppingSessionState.groupedClientTracks.indexOf(from);
                    stoppingSessionState.groupedClientTracks.splice(index, 1);

                    if(stoppingSessionState.groupedClientTracks.length == 0) {
                        finishSuccessfulStop(payload.recordingId);
                    }
                }
                else {
                    // TOOD: a client responded with error; what now?
                    logger.error("client responded with error: ", payload);
                }
            }
            else {
                // TODO: this is an error case; we should tell the caller we have no recording at the moment
            }
        }

        function onAbortRecording(from, payload) {
            logger.debug("received abort recording from " + from);

            // TODO check if currently recording and if matches payload.recordingId

            // if creator, tell everyone else to stop
            if(app.clientId == currentRecordingCreatorClientId) {
                // ask the front end to stop the recording because it has the full track listing
                for(var i = 0; i < currentRecordingClientIds.length; i++) {
                    var clientId = currentRecordingClientIds[i];
                    context.JK.JamServer.sendP2PMessage(clientId, JSON.stringify(p2pMessageFactory.abortRecording(currentRecordingId, payload.reason, from)));
                }

            }
            else {
                logger.debug("only the creator currently deals with the abort request.  abort request sent from:" + from + " with a reason of: " + payload.errorReason);
            }

            eval(abortedRecordingEventCallbackName).call(this, payload.recordingId, {success:payload.success, reason:payload.reason, detail:from});
        }

        function RegisterRecordingCallbacks(startRecordingCallbackName,
                                            stopRecordingCallbackName,
                                            startedRecordingCallbackName,
                                            stoppedRecordingCallbackName,
                                            abortedRecordingCallbackName) {
            startRecordingResultCallbackName = startRecordingCallbackName;
            stopRecordingResultCallbackName = stopRecordingCallbackName;
            startedRecordingResultCallbackName = startedRecordingCallbackName;
            stoppedRecordingEventCallbackName = stoppedRecordingCallbackName;
            abortedRecordingEventCallbackName = abortedRecordingCallbackName;
        }

        // copies all clientIds, but removes current client ID because we don't want to message that user
        function copyClientIds(clientIds, myClientId) {
            var newClientIds = [];
            for(var i = 0; i < clientIds.length; i++) {
                var clientId = clientIds[i]
                if(clientId != myClientId) {
                    newClientIds.push(clientId);
                }
            }
            return newClientIds;
        }

        function finishSuccessfulStart(recordingId) {
            // all clients have responded.
            clearTimeout(startingSessionState.aggegratingStartResultsTimer);
            startingSessionState = null;
            eval(startRecordingResultCallbackName).call(this, recordingId, {success:true});
        }

        function finishSuccessfulStop(recordingId, errorReason) {
            // all clients have responded.
            clearTimeout(stoppingSessionState.aggegratingStopResultsTimer);
            stoppingSessionState = null;
            var result = { success: true }
            if(errorReason)
            {
                result.success = false;
                result.reason = errorReason
                result.detail = ""
            }
            eval(stopRecordingResultCallbackName).call(this, recordingId, result);
        }


        // register for p2p callbacks
        var callbacks = {};
        callbacks[p2pMessageFactory.Types.START_RECORDING] = onStartRecording;
        callbacks[p2pMessageFactory.Types.START_RECORDING_ACK] = onStartRecordingAck;
        callbacks[p2pMessageFactory.Types.STOP_RECORDING] = onStopRecording;
        callbacks[p2pMessageFactory.Types.STOP_RECORDING_ACK] = onStopRecordingAck;
        callbacks[p2pMessageFactory.Types.ABORT_RECORDING] = onAbortRecording;
        fakeJamClient.RegisterP2PMessageCallbacks(callbacks);
        this.StartRecording = StartRecording;
        this.StopRecording = StopRecording;
        this.AbortRecording = AbortRecording;
        this.RegisterRecordingCallbacks = RegisterRecordingCallbacks;
    }

    })(window, jQuery);