/* global APP, $, config, interfaceConfig, JitsiMeetJS */
/*
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from '../util/UIUtil';
import VideoLayout from '../videolayout/VideoLayout';
import Feedback from '../feedback/Feedback.js';
import Toolbar from '../toolbars/Toolbar';

/**
 * The dialog for user input.
 */
let dialog = null;

/**
 * Indicates if the recording button should be enabled.
 *
 * @returns {boolean} {true} if the
 * @private
 */
function _isRecordingButtonEnabled() {
    return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
            && config.enableRecording && APP.conference.isRecordingSupported();
}

/**
 * Request live stream token from the user.
 * @returns {Promise}
 */
function _requestLiveStreamId() {
    const msg = APP.translation.generateTranslationHTML("dialog.liveStreaming");
    const token = APP.translation.translateString("dialog.streamKey");
    const cancelButton
        = APP.translation.generateTranslationHTML("dialog.Cancel");
    const backButton = APP.translation.generateTranslationHTML("dialog.Back");
    const startStreamingButton
        = APP.translation.generateTranslationHTML("dialog.startLiveStreaming");
    const streamIdRequired
        = APP.translation.generateTranslationHTML(
            "liveStreaming.streamIdRequired");

    return new Promise(function (resolve, reject) {
        dialog = APP.UI.messageHandler.openDialogWithStates({
            state0: {
                html:
                    `<h2>${msg}</h2>
                    <input name="streamId" type="text"
                    data-i18n="[placeholder]dialog.streamKey"
                    placeholder="${token}" autofocus>`,
                persistent: false,
                buttons: [
                    {title: cancelButton, value: false},
                    {title: startStreamingButton, value: true}
                ],
                focus: ':input:first',
                defaultButton: 1,
                submit: function (e, v, m, f) {
                    e.preventDefault();

                    if (v) {
                        if (f.streamId && f.streamId.length > 0) {
                            resolve(UIUtil.escapeHtml(f.streamId));
                            dialog.close();
                            return;
                        }
                        else {
                            dialog.goToState('state1');
                            return false;
                        }
                    } else {
                        reject(APP.UI.messageHandler.CANCEL);
                        dialog.close();
                        return false;
                    }
                }
            },

            state1: {
                html: `<h2>${msg}</h2> ${streamIdRequired}`,
                persistent: false,
                buttons: [
                    {title: cancelButton, value: false},
                    {title: backButton, value: true}
                ],
                focus: ':input:first',
                defaultButton: 1,
                submit: function (e, v, m, f) {
                    e.preventDefault();
                    if (v === 0) {
                        reject(APP.UI.messageHandler.CANCEL);
                        dialog.close();
                    } else {
                        dialog.goToState('state0');
                    }
                }
            }
        }, {
            close: function () {
                dialog = null;
            }
        });
    });
}

/**
 * Request recording token from the user.
 * @returns {Promise}
 */
function _requestRecordingToken () {
    let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
    let token = APP.translation.translateString("dialog.token");

    return new Promise(function (resolve, reject) {
        dialog = APP.UI.messageHandler.openTwoButtonDialog(
            null, null, null,
            `<h2>${msg}</h2>
             <input name="recordingToken" type="text"
                    data-i18n="[placeholder]dialog.token"
                    placeholder="${token}" autofocus>`,
            false, "dialog.Save",
            function (e, v, m, f) {
                if (v && f.recordingToken) {
                    resolve(UIUtil.escapeHtml(f.recordingToken));
                } else {
                    reject(APP.UI.messageHandler.CANCEL);
                }
            },
            null,
            function () {
                dialog = null;
            },
            ':input:first'
        );
    });
}

/**
 * Shows a prompt dialog to the user when they have toggled off the recording.
 *
 * @param recordingType the recording type
 * @returns {Promise}
 * @private
 */
function _showStopRecordingPrompt (recordingType) {
    var title;
    var message;
    var buttonKey;
    if (recordingType === "jibri") {
        title = "dialog.liveStreaming";
        message = "dialog.stopStreamingWarning";
        buttonKey = "dialog.stopLiveStreaming";
    }
    else {
        title = "dialog.recording";
        message = "dialog.stopRecordingWarning";
        buttonKey = "dialog.stopRecording";
    }

    return new Promise(function (resolve, reject) {
        dialog = APP.UI.messageHandler.openTwoButtonDialog(
            title,
            null,
            message,
            null,
            false,
            buttonKey,
            function(e,v,m,f) {
                if (v) {
                    resolve();
                } else {
                    reject();
                }
            },
            null,
            function () {
                dialog = null;
            }
        );
    });
}

/**
 * Moves the element given by {selector} to the top right corner of the screen.
 * @param selector the selector for the element to move
 * @param move {true} to move the element, {false} to move it back to its intial
 * position
 */
function moveToCorner(selector, move) {
    let moveToCornerClass = "moveToCorner";
    let containsClass = selector.hasClass(moveToCornerClass);

    if (move && !containsClass)
        selector.addClass(moveToCornerClass);
    else if (!move && containsClass)
        selector.removeClass(moveToCornerClass);
}

/**
 * The status of the recorder.
 * FIXME: Those constants should come from the library.
 * @type {{ON: string, OFF: string, AVAILABLE: string,
 * UNAVAILABLE: string, PENDING: string}}
 */
var Status = {
    ON: "on",
    OFF: "off",
    AVAILABLE: "available",
    UNAVAILABLE: "unavailable",
    PENDING: "pending",
    RETRYING: "retrying",
    ERROR: "error",
    FAILED: "failed",
    BUSY: "busy"
};

/**
 * Checks whether if the given status is either PENDING or RETRYING
 * @param status {Status} Jibri status to be checked
 * @returns {boolean} true if the condition is met or false otherwise.
 */
function isStartingStatus(status) {
    return status === Status.PENDING || status === Status.RETRYING;
}

/**
 * Manages the recording user interface and user experience.
 * @type {{init, initRecordingButton, showRecordingButton, updateRecordingState,
 * updateRecordingUI, checkAutoRecord}}
 */
var Recording = {
    /**
     * Initializes the recording UI.
     */
    init (emitter, recordingType) {
        this.eventEmitter = emitter;

        this.updateRecordingState(APP.conference.getRecordingState());

        this.initRecordingButton(recordingType);

        // If I am a recorder then I publish my recorder custom role to notify
        // everyone.
        if (config.iAmRecorder) {
            VideoLayout.enableDeviceAvailabilityIcons(
                APP.conference.getMyUserId(), false);
            VideoLayout.setLocalVideoVisible(false);
            Feedback.enableFeedback(false);
            Toolbar.enable(false);
            APP.UI.messageHandler.enableNotifications(false);
            APP.UI.messageHandler.enablePopups(false);
        }
    },

    /**
     * Initialise the recording button.
     */
    initRecordingButton(recordingType) {
        let selector = $('#toolbar_button_record');

        let button = selector.get(0);
        UIUtil.setTooltip(button, 'liveStreaming.buttonTooltip', 'right');

        if (recordingType === 'jibri') {
            this.baseClass = "fa fa-play-circle";
            this.recordingTitle = "dialog.liveStreaming";
            this.recordingOnKey = "liveStreaming.on";
            this.recordingOffKey = "liveStreaming.off";
            this.recordingPendingKey = "liveStreaming.pending";
            this.failedToStartKey = "liveStreaming.failedToStart";
            this.recordingErrorKey = "liveStreaming.error";
            this.recordingButtonTooltip = "liveStreaming.buttonTooltip";
            this.recordingUnavailable = "liveStreaming.unavailable";
            this.recordingBusy = "liveStreaming.busy";
        }
        else {
            this.baseClass = "icon-recEnable";
            this.recordingTitle = "dialog.recording";
            this.recordingOnKey = "recording.on";
            this.recordingOffKey = "recording.off";
            this.recordingPendingKey = "recording.pending";
            this.failedToStartKey = "recording.failedToStart";
            this.recordingErrorKey = "recording.error";
            this.recordingButtonTooltip = "recording.buttonTooltip";
            this.recordingUnavailable = "recording.unavailable";
            this.recordingBusy = "liveStreaming.busy";
        }

        selector.addClass(this.baseClass);
        selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
        selector.attr("content",
            APP.translation.translateString(this.recordingButtonTooltip));

        var self = this;
        selector.click(function () {
            if (dialog)
                return;
            JitsiMeetJS.analytics.sendEvent('recording.clicked');
            switch (self.currentState) {
                case Status.ON:
                case Status.RETRYING:
                case Status.PENDING: {
                    _showStopRecordingPrompt(recordingType).then(
                        () => {
                            self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
                            JitsiMeetJS.analytics.sendEvent(
                                'recording.stopped');
                        },
                        () => {});
                    break;
                }
                case Status.AVAILABLE:
                case Status.OFF: {
                    if (recordingType === 'jibri')
                        _requestLiveStreamId().then((streamId) => {
                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
                                {streamId: streamId});
                            JitsiMeetJS.analytics.sendEvent(
                                'recording.started');
                        }).catch(
                            reason => {
                                if (reason !== APP.UI.messageHandler.CANCEL)
                                    console.error(reason);
                                else
                                    JitsiMeetJS.analytics.sendEvent(
                                        'recording.canceled');
                            }
                        );
                    else {
                        if (self.predefinedToken) {
                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
                                {token: self.predefinedToken});
                            JitsiMeetJS.analytics.sendEvent(
                                'recording.started');
                            return;
                        }

                        _requestRecordingToken().then((token) => {
                            self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
                                {token: token});
                            JitsiMeetJS.analytics.sendEvent(
                                'recording.started');
                        }).catch(
                            reason => {
                                if (reason !== APP.UI.messageHandler.CANCEL)
                                    console.error(reason);
                                else
                                    JitsiMeetJS.analytics.sendEvent(
                                        'recording.canceled');
                            }
                        );
                    }
                    break;
                }
                case Status.BUSY: {
                    dialog = APP.UI.messageHandler.openMessageDialog(
                        self.recordingTitle,
                        self.recordingBusy,
                        null, null,
                        function () {
                            dialog = null;
                        }
                    );
                    break;
                }
                default: {
                    dialog = APP.UI.messageHandler.openMessageDialog(
                        self.recordingTitle,
                        self.recordingUnavailable,
                        null, null,
                        function () {
                            dialog = null;
                        }
                    );
                }
            }
        });
    },

    /**
     * Shows or hides the 'recording' button.
     * @param show {true} to show the recording button, {false} to hide it
     */
    showRecordingButton (show) {
        if (_isRecordingButtonEnabled() && show) {
            $('#toolbar_button_record').css({display: "inline-block"});
        } else {
            $('#toolbar_button_record').css({display: "none"});
        }
    },

    /**
     * Updates the recording state UI.
     * @param recordingState gives us the current recording state
     */
    updateRecordingState(recordingState) {
        // I'm the recorder, so I don't want to see any UI related to states.
        if (config.iAmRecorder)
            return;

        // If there's no state change, we ignore the update.
        if (!recordingState || this.currentState === recordingState)
            return;

        this.updateRecordingUI(recordingState);
    },

    /**
     * Sets the state of the recording button.
     * @param recordingState gives us the current recording state
     */
    updateRecordingUI (recordingState) {
        let buttonSelector = $('#toolbar_button_record');

        let oldState = this.currentState;
        this.currentState = recordingState;

        // TODO: handle recording state=available
        if (recordingState === Status.ON ||
            recordingState === Status.RETRYING) {

            buttonSelector.removeClass(this.baseClass);
            buttonSelector.addClass(this.baseClass + " active");

            this._updateStatusLabel(this.recordingOnKey, false);
        }
        else if (recordingState === Status.OFF
                || recordingState === Status.UNAVAILABLE
                || recordingState === Status.BUSY
                || recordingState === Status.FAILED) {

            // We don't want to do any changes if this is
            // an availability change.
            if (oldState !== Status.ON
                && !isStartingStatus(oldState))
                return;

            buttonSelector.removeClass(this.baseClass + " active");
            buttonSelector.addClass(this.baseClass);

            let messageKey;
            if (isStartingStatus(oldState))
                messageKey = this.failedToStartKey;
            else
                messageKey = this.recordingOffKey;

            this._updateStatusLabel(messageKey, true);

            setTimeout(function(){
                $('#recordingLabel').css({display: "none"});
            }, 5000);
        }
        else if (recordingState === Status.PENDING) {

            buttonSelector.removeClass(this.baseClass + " active");
            buttonSelector.addClass(this.baseClass);

            this._updateStatusLabel(this.recordingPendingKey, true);
        }
        else if (recordingState === Status.ERROR
                    || recordingState === Status.FAILED) {
            buttonSelector.removeClass(this.baseClass + " active");
            buttonSelector.addClass(this.baseClass);

            this._updateStatusLabel(this.recordingErrorKey, true);
        }

        let labelSelector = $('#recordingLabel');

        // We don't show the label for available state.
        if (recordingState !== Status.AVAILABLE
            && !labelSelector.is(":visible"))
            labelSelector.css({display: "inline-block"});

        // Recording spinner
        if (recordingState === Status.RETRYING)
            $("#recordingSpinner").show();
        else
            $("#recordingSpinner").hide();
    },
    // checks whether recording is enabled and whether we have params
    // to start automatically recording
    checkAutoRecord () {
        if (_isRecordingButtonEnabled && config.autoRecord) {
            this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
            this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
                                    this.predefinedToken);
        }
    },
    /**
     * Updates the status label.
     * @param textKey the text to show
     * @param isCentered indicates if the label should be centered on the window
     * or moved to the top right corner.
     */
    _updateStatusLabel(textKey, isCentered) {
        let labelSelector = $('#recordingLabel');
        let labelTextSelector = $('#recordingLabelText');

        moveToCorner(labelSelector, !isCentered);

        labelTextSelector.attr("data-i18n", textKey);
        labelTextSelector.text(APP.translation.translateString(textKey));
    }
};

export default Recording;
