'use strict';
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));
var prop_types_1 = __importDefault(require("prop-types"));
var classnames_1 = __importDefault(require("classnames"));
var lodash_1 = __importDefault(require("lodash"));
var Icon_1 = require("../Icon");
var Block_1 = require("../Block");
var DangerousHTML_1 = require("../DangerousHTML");
var utils_1 = require("./utils");
var RecorderLib_1 = require("./RecorderLib");
var VolumeControl_1 = require("./VolumeControl");
var AudioPlayer_1 = require("./AudioPlayer");
var Button_1 = require("../Button");
var index_en_US_1 = __importDefault(require("../../../lang/common/index-en_US"));
var METER_FULL_VALUE = 28;
var METER_BOUNDARY = 5;
var RecorderVolumeMeter = /** @class */ (function (_super) {
    __extends(RecorderVolumeMeter, _super);
    function RecorderVolumeMeter(props) {
        var _this = _super.call(this, props) || this;
        _this.setInputMaxVolume = function (value, isMuted) {
            var volumeGainNode = _this.props.volumeGainNode;
            var volumeNodeGainValue = _this.state.volumeNodeGainValue;
            if (lodash_1.default.isUndefined(isMuted)) {
                if (!value) {
                    value = volumeNodeGainValue;
                    isMuted = true;
                }
                else {
                    isMuted = false;
                }
            }
            else if (!isMuted) {
                value = value || 1;
            }
            volumeGainNode.gain.value = isMuted ? 0 : value;
            _this.setState({
                volumeNodeGainValue: value,
                isMuted: isMuted,
                reset: true
            });
        };
        var analyser = props.analyser, scriptProcessorNode = props.scriptProcessorNode, volumeGainNode = props.volumeGainNode, meterCount = props.meterCount;
        var volumeNodeGainValue = volumeGainNode.gain.value;
        _this.ended = false;
        _this.data = new Array(meterCount);
        _this.state = {
            volume: 1,
            reset: false,
            volumeNodeGainValue: volumeNodeGainValue,
            isMuted: volumeNodeGainValue === 0
        };
        scriptProcessorNode.onaudioprocess = function () {
            if (_this.ended) {
                return;
            }
            var _a = _this.state, reset = _a.reset, volume = _a.volume, isMuted = _a.isMuted, volumeNodeGainValue = _a.volumeNodeGainValue;
            var currentVolume;
            if (isMuted) {
                volumeNodeGainValue = 0;
            }
            if (volumeNodeGainValue === 0) {
                currentVolume = 0;
            }
            else {
                currentVolume = RecorderLib_1.getRecorderInputVolume(analyser, volume, reset);
            }
            _this.setState({
                volume: currentVolume,
                reset: false
            });
        };
        return _this;
    }
    RecorderVolumeMeter.prototype.componentWillUnmount = function () {
        this.ended = true;
    };
    RecorderVolumeMeter.prototype.getRecordButton = function () {
        var _this = this;
        return (react_1.default.createElement(Button_1.Button, { "aria-label": this.props.startRecordingLabel, className: "rc-audio-icon-wrapper rc-audio-record", testAutomationId: "audioRecord" + this.props.id, onClick: function () {
                _this.props.startRecording();
            } },
            react_1.default.createElement(Icon_1.Icon, { name: "record" })));
    };
    RecorderVolumeMeter.prototype.getMeter = function () {
        var volume = this.state.volume;
        var meterCount = this.props.meterCount;
        var volumeCount = Math.min(volume / (METER_FULL_VALUE / meterCount), meterCount);
        var className = volumeCount < METER_BOUNDARY ? 'volume-unqualified' : 'volume-qualified';
        for (var i = 0; i < meterCount; i++) {
            //init value
            this.data[i] = i <= volumeCount && volume > 0;
        }
        return (react_1.default.createElement("div", { className: "volume-meter" }, lodash_1.default.map(this.data, function (enabled, index) {
            var qualifiedClass = {};
            qualifiedClass[className] = enabled;
            return (react_1.default.createElement("div", { className: classnames_1.default('volume-meter-unit', qualifiedClass), key: "volume-meter-unit-" + (index + 1) }));
        })));
    };
    RecorderVolumeMeter.prototype.getVolumeControl = function () {
        var _a = this.state, volumeNodeGainValue = _a.volumeNodeGainValue, isMuted = _a.isMuted;
        return (react_1.default.createElement(VolumeControl_1.VolumeControl, { volume: volumeNodeGainValue, isMuted: isMuted, setVolume: this.setInputMaxVolume }));
    };
    RecorderVolumeMeter.prototype.render = function () {
        return (react_1.default.createElement("div", { className: "rc-audio-recorder" },
            this.getRecordButton(),
            this.getMeter(),
            this.getVolumeControl()));
    };
    RecorderVolumeMeter.propTypes = {
        id: prop_types_1.default.string,
        scriptProcessorNode: prop_types_1.default.object,
        volumeGainNode: prop_types_1.default.object,
        analyser: prop_types_1.default.object,
        meterCount: prop_types_1.default.number,
        startRecording: prop_types_1.default.func,
        startRecordingLabel: prop_types_1.default.string
    };
    RecorderVolumeMeter.defaultProps = {
        id: '',
        analyser: null,
        scriptProcessorNode: null,
        volumeGainNode: null,
        startRecording: function () { },
        meterCount: 12,
        get startRecordingLabel() {
            return index_en_US_1.default.START_RECORDING;
        }
    };
    return RecorderVolumeMeter;
}(react_1.default.Component));
exports.RecorderVolumeMeter = RecorderVolumeMeter;
var PROGRESS_WAVE = 'progress-wave';
var RecorderProcessor = /** @class */ (function (_super) {
    __extends(RecorderProcessor, _super);
    function RecorderProcessor(props) {
        var _this = _super.call(this, props) || this;
        _this.state = {
            inputVolume: 0,
            currentTime: 0
        };
        _this.initTime = 0;
        _this.data = [];
        return _this;
    }
    RecorderProcessor.prototype.componentWillMount = function () {
        var _this = this;
        var _a = this.props, recorder = _a.recorder, scriptProcessorNode = _a.scriptProcessorNode, analyser = _a.analyser, stopRecording = _a.stopRecording, maxRecordTime = _a.maxRecordTime;
        scriptProcessorNode.onaudioprocess = function (e) {
            recorder.onaudioprocess(e);
            if (!recorder.recording) {
                return;
            }
            var inputVolume = _this.state.inputVolume;
            var currentTime = e.playbackTime;
            if (!_this.initTime) {
                _this.initTime = currentTime;
            }
            currentTime -= _this.initTime;
            if (currentTime < maxRecordTime) {
                inputVolume = RecorderLib_1.getRecorderInputVolume(analyser, inputVolume, false);
                _this.data.push(inputVolume);
                if (_this.maxMeterCount && _this.data.length > _this.maxMeterCount) {
                    _this.data.shift();
                }
                if (!_this.unmounted) {
                    _this.setState({
                        inputVolume: inputVolume,
                        currentTime: currentTime
                    });
                }
            }
            else {
                if (_this.unmounted) {
                    stopRecording();
                }
            }
        };
        recorder.record();
    };
    RecorderProcessor.prototype.componentDidMount = function () {
        var progressWaveLength = this.refs[PROGRESS_WAVE].offsetWidth;
        this.maxMeterCount = (progressWaveLength + 1) / 2;
    };
    RecorderProcessor.prototype.componentWillUnmount = function () {
        this.unmounted = true;
    };
    RecorderProcessor.prototype.getStopRecordButton = function () {
        var _this = this;
        return (react_1.default.createElement(Button_1.Button, { "aria-label": this.props.stopRecordingLabel, className: "rc-audio-icon-wrapper rc-record-complete", testAutomationId: "audioStopRecord" + this.props.id, onClick: function () {
                _this.props.stopRecording();
            } },
            react_1.default.createElement(Icon_1.Icon, { name: "record-complete" })));
    };
    RecorderProcessor.prototype.getWave = function () {
        var _a = this.props, meterMaxHeight = _a.meterMaxHeight, meterMinHeight = _a.meterMinHeight;
        var progressedMeter = lodash_1.default.map(this.data, function (volume, index) {
            var height = meterMinHeight +
                (Math.min(volume, METER_FULL_VALUE) / METER_FULL_VALUE) *
                    (meterMaxHeight - meterMinHeight);
            return (react_1.default.createElement("div", { className: "progress-volume-meter", style: { left: index * 2 + 'px', height: height + 'px' }, key: "progress-volume-meter-unit-" + index }));
        });
        return (react_1.default.createElement("div", { className: "progress-wav", ref: PROGRESS_WAVE }, progressedMeter));
    };
    RecorderProcessor.prototype.render = function () {
        var currentTime = this.state.currentTime;
        return (react_1.default.createElement("div", { className: "rc-audio-recorder recorder-progressor" },
            this.getStopRecordButton(),
            this.getWave(),
            react_1.default.createElement("div", { className: "audio-duration" },
                utils_1.formatSeconds(currentTime),
                " / ",
                utils_1.formatSeconds(this.props.maxRecordTime))));
    };
    RecorderProcessor.propTypes = {
        id: prop_types_1.default.string,
        analyser: prop_types_1.default.object,
        recorder: prop_types_1.default.object,
        scriptProcessorNode: prop_types_1.default.object,
        meterMinHeight: prop_types_1.default.number,
        meterMaxHeight: prop_types_1.default.number,
        maxRecordTime: prop_types_1.default.number,
        stopRecording: prop_types_1.default.func,
        stopRecordingLabel: prop_types_1.default.string
    };
    RecorderProcessor.defaultProps = {
        id: '',
        analyser: null,
        recorder: null,
        scriptProcessorNode: null,
        stopRecording: function () { },
        meterMaxHeight: 15,
        meterMinHeight: 5,
        maxRecordTime: 300,
        get stopRecordingLabel() {
            return index_en_US_1.default.STOP_RECORDING;
        }
    };
    return RecorderProcessor;
}(react_1.default.Component));
exports.RecorderProcessor = RecorderProcessor;
var STEPS = {
    METER: 0,
    RECORDING: 1,
    RECORDED: 2
};
var AudioRecorder = /** @class */ (function (_super) {
    __extends(AudioRecorder, _super);
    function AudioRecorder(props) {
        var _this = _super.call(this, props) || this;
        _this.state = {
            step: STEPS.METER,
            ready: false
        };
        return _this;
    }
    AudioRecorder.prototype.componentWillMount = function () {
        var _this = this;
        if (audioRecorderSupported()) {
            var _a = this.props, audioRecordError_1 = _a.audioRecordError, deviceNotFoundError_1 = _a.deviceNotFoundError;
            window['audioContext'] = window['audioContext'] || new AudioContext();
            this.audioContext = window['audioContext'];
            navigator.mediaDevices
                .getUserMedia({ audio: true })
                .then(function (stream) {
                _this.localStream = stream;
                var config = _this.props.config;
                _this.mediaStream = _this.audioContext.createMediaStreamSource(stream);
                _this.volumeGainNode = _this.audioContext.createGain();
                _this.mediaStream.connect(_this.volumeGainNode);
                _this.analyser = _this.audioContext.createAnalyser();
                _this.analyser.fftSize = config.bufferLen / config.numChannels;
                _this.volumeGainNode.connect(_this.analyser);
                _this.scriptProcessorNode = _this.audioContext.createScriptProcessor(config.bufferLen, config.numChannels, config.numChannels);
                _this.analyser.connect(_this.scriptProcessorNode);
                _this.scriptProcessorNode.connect(_this.audioContext.destination); //this should not be necessary
                _this.setState({
                    ready: true
                });
            })
                .catch(function (e) {
                if (e.name === 'DevicesNotFoundError' || //for Chrome
                    e.name === 'NotFoundError') {
                    //for firefox
                    _this.errorText = deviceNotFoundError_1;
                }
                else {
                    _this.errorText = audioRecordError_1 || e.name;
                }
                // eslint-disable-next-line
                console.log(e);
                _this.setState({
                    ready: true
                });
            });
        }
    };
    AudioRecorder.prototype.componentWillUnmount = function () {
        if (audioRecorderSupported() && this.mediaStream) {
            this.mediaStream.disconnect(this.volumeGainNode);
            this.volumeGainNode.disconnect(this.analyser);
            this.analyser.disconnect(this.scriptProcessorNode);
            this.scriptProcessorNode.disconnect(this.audioContext.destination);
            delete this.scriptProcessorNode;
            delete this.audioContext;
            lodash_1.default.forEach(this.localStream.getTracks(), function (track) {
                track.stop();
            });
        }
    };
    AudioRecorder.prototype.getText = function (title, text) {
        var titleCmp = title ? (react_1.default.createElement(Block_1.Block, { condensed: true },
            react_1.default.createElement("strong", null, title))) : null;
        return (react_1.default.createElement("div", null,
            titleCmp,
            react_1.default.createElement(Block_1.Block, null,
                react_1.default.createElement(DangerousHTML_1.DangerousHTML, null, text))));
    };
    AudioRecorder.prototype.getUploadButton = function () {
        var _this = this;
        return this.state.step === STEPS.RECORDED ? (react_1.default.createElement("div", { className: "rc-audio-upload" },
            react_1.default.createElement("a", { role: "button", "aria-label": this.props.uploadLabel, href: "#", onClick: function (e) {
                    e.preventDefault();
                    _this.uploadAudio();
                } },
                react_1.default.createElement(Icon_1.Icon, { name: "circle-upload" })))) : null;
    };
    AudioRecorder.prototype.createRecorder = function () {
        return new RecorderLib_1.RecorderLib(this.props.config, this.audioContext.sampleRate);
    };
    AudioRecorder.prototype.startRecording = function () {
        this.recorder = this.recorder || this.createRecorder();
        this.setState({
            step: STEPS.RECORDING
        });
    };
    AudioRecorder.prototype.stopRecording = function () {
        var _this = this;
        if (this.recorder) {
            this.recorder.stop();
            this.recorder.exportWAV(function (audioBlobData) {
                var onRecordingStopped = _this.props.onRecordingStopped;
                if (onRecordingStopped) {
                    onRecordingStopped(audioBlobData);
                }
                _this.audioBlobData = audioBlobData;
                _this.recorder.clear();
                _this.setState({
                    step: STEPS.RECORDED
                });
            });
        }
    };
    AudioRecorder.prototype.uploadAudio = function () {
        if (this.audioBlobData) {
            var _a = this.props, exportAudio = _a.exportAudio, fileName = _a.fileName;
            var formData = new FormData();
            formData.append('file', this.audioBlobData, fileName);
            if (exportAudio) {
                exportAudio(formData);
            }
        }
    };
    AudioRecorder.prototype.render = function () {
        var _this = this;
        var _a = this.props, unsupportText = _a.unsupportText, volumeMeterTitle = _a.volumeMeterTitle, volumeMeterText = _a.volumeMeterText, processorTitle = _a.processorTitle, processorText = _a.processorText, completedTitle = _a.completedTitle, completedText = _a.completedText;
        var _b = this.state, ready = _b.ready, step = _b.step;
        if (!audioRecorderSupported()) {
            return this.getText('', unsupportText);
        }
        if (!ready) {
            return null;
        }
        if (this.errorText) {
            return this.getText('', this.errorText);
        }
        var recorderStep = null;
        var promptText = null;
        switch (step) {
            case STEPS.METER:
                promptText = this.getText(volumeMeterTitle, volumeMeterText);
                recorderStep = (react_1.default.createElement(RecorderVolumeMeter, { scriptProcessorNode: this.scriptProcessorNode, volumeGainNode: this.volumeGainNode, analyser: this.analyser, startRecording: function () {
                        _this.startRecording();
                    } }));
                break;
            case STEPS.RECORDING:
                promptText = this.getText(processorTitle, processorText);
                recorderStep = (react_1.default.createElement(RecorderProcessor, { scriptProcessorNode: this.scriptProcessorNode, analyser: this.analyser, recorder: this.recorder, stopRecording: function () {
                        _this.stopRecording();
                    } }));
                break;
            case STEPS.RECORDED:
                promptText = this.getText(completedTitle, completedText);
                var audioUrl = this.audioBlobData ? URL.createObjectURL(this.audioBlobData) : '';
                recorderStep = audioUrl ? (react_1.default.createElement(AudioPlayer_1.AudioPlayer, { audioUrl: audioUrl, onRecord: function () {
                        _this.setState({
                            step: STEPS.METER
                        });
                    } })) : null;
                break;
            default:
                break;
        }
        return (react_1.default.createElement("div", null,
            promptText,
            react_1.default.createElement("div", { className: "rc-audio" },
                recorderStep,
                this.getUploadButton())));
    };
    AudioRecorder.propTypes = {
        unsupportText: prop_types_1.default.string,
        volumeMeterTitle: prop_types_1.default.string,
        volumeMeterText: prop_types_1.default.string,
        processorTitle: prop_types_1.default.string,
        processorText: prop_types_1.default.string,
        completedTitle: prop_types_1.default.string,
        completedText: prop_types_1.default.string,
        fileName: prop_types_1.default.string,
        audioRecordError: prop_types_1.default.string,
        deviceNotFoundError: prop_types_1.default.string,
        config: prop_types_1.default.object,
        exportAudio: prop_types_1.default.func,
        onRecordingStopped: prop_types_1.default.func,
        uploadLabel: prop_types_1.default.string
    };
    AudioRecorder.defaultProps = {
        exportAudio: null,
        onRecordingStopped: null,
        config: {
            bufferLen: 2048,
            numChannels: 1,
            mimeType: 'audio/wav'
        },
        fileName: 'audio.wav',
        id: 'audio-recorder',
        get unsupportText() {
            return index_en_US_1.default.MICROPHONE_NOT_SUPPORTED;
        },
        get volumeMeterTitle() {
            return index_en_US_1.default.MICROPHONE_TEST;
        },
        get volumeMeterText() {
            return index_en_US_1.default.MICROPHONE_TEST_TEXT;
        },
        get processorTitle() {
            return index_en_US_1.default.RECORDING;
        },
        get processorText() {
            return index_en_US_1.default.RECORDING_IN_PROGRESS;
        },
        get completedTitle() {
            return index_en_US_1.default.RECORDING;
        },
        get completedText() {
            return index_en_US_1.default.MICROPHONE_RECORD_COMPLETE;
        },
        get deviceNotFoundError() {
            return index_en_US_1.default.MICROPHONE_NOT_FOUND_ERROR;
        },
        get audioRecordError() {
            return index_en_US_1.default.MICROPHONE_RECORD_ERROR;
        },
        get uploadLabel() {
            return index_en_US_1.default.UPLOAD;
        }
    };
    return AudioRecorder;
}(react_1.default.Component));
exports.AudioRecorder = AudioRecorder;
function audioRecorderSupported() {
    window['AudioContext'] = window['AudioContext'] || window['webkitAudioContext'];
    window.URL = window.URL || window['webkitURL'];
    return (window['AudioContext'] &&
        window.URL &&
        navigator.mediaDevices &&
        navigator.mediaDevices.getUserMedia);
}
exports.audioRecorderSupported = audioRecorderSupported;
//# sourceMappingURL=AudioRecorder.js.map