'use strict';

import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import _ from 'lodash';
import { Icon } from '../Icon';
import { Block } from '../Block';
import { DangerousHTML } from '../DangerousHTML';
import { formatSeconds } from './utils';
import { RecorderLib, getRecorderInputVolume } from './RecorderLib';
import { VolumeControl } from './VolumeControl';
import { AudioPlayer } from './AudioPlayer';
import { Button } from '../Button';
var METER_FULL_VALUE = 28;
var METER_BOUNDARY = 5;
export var RecorderVolumeMeter =
/*#__PURE__*/
function (_React$Component) {
  _inheritsLoose(RecorderVolumeMeter, _React$Component);

  function RecorderVolumeMeter(props) {
    var _this;

    _this = _React$Component.call(this, props) || this;

    _defineProperty(_assertThisInitialized(_this), "setInputMaxVolume", function (value, isMuted) {
      var volumeGainNode = _this.props.volumeGainNode;
      var volumeNodeGainValue = _this.state.volumeNodeGainValue;

      if (_.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 _this$state = _this.state,
          reset = _this$state.reset,
          volume = _this$state.volume,
          isMuted = _this$state.isMuted,
          volumeNodeGainValue = _this$state.volumeNodeGainValue;
      var currentVolume;

      if (isMuted) {
        volumeNodeGainValue = 0;
      }

      if (volumeNodeGainValue === 0) {
        currentVolume = 0;
      } else {
        currentVolume = getRecorderInputVolume(analyser, volume, reset);
      }

      _this.setState({
        volume: currentVolume,
        reset: false
      });
    };

    return _this;
  }

  var _proto = RecorderVolumeMeter.prototype;

  _proto.componentWillUnmount = function componentWillUnmount() {
    this.ended = true;
  };

  _proto.getRecordButton = function getRecordButton() {
    var _this2 = this;

    return React.createElement(Button, {
      "aria-label": this.props.startRecordingLabel,
      className: "rc-audio-icon-wrapper rc-audio-record",
      testAutomationId: "audioRecord" + this.props.id,
      onClick: function onClick() {
        _this2.props.startRecording();
      }
    }, React.createElement(Icon, {
      name: "record"
    }));
  };

  _proto.getMeter = function getMeter() {
    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.createElement("div", {
      className: "volume-meter"
    }, _.map(this.data, function (enabled, index) {
      var qualifiedClass = {};
      qualifiedClass[className] = enabled;
      return React.createElement("div", {
        className: classnames('volume-meter-unit', qualifiedClass),
        key: "volume-meter-unit-" + (index + 1)
      });
    }));
  };

  _proto.getVolumeControl = function getVolumeControl() {
    var _this$state2 = this.state,
        volumeNodeGainValue = _this$state2.volumeNodeGainValue,
        isMuted = _this$state2.isMuted;
    return React.createElement(VolumeControl, {
      volume: volumeNodeGainValue,
      isMuted: isMuted,
      setVolume: this.setInputMaxVolume
    });
  };

  _proto.render = function render() {
    return React.createElement("div", {
      className: "rc-audio-recorder"
    }, this.getRecordButton(), this.getMeter(), this.getVolumeControl());
  };

  return RecorderVolumeMeter;
}(React.Component);

_defineProperty(RecorderVolumeMeter, "propTypes", {
  id: PropTypes.string,
  scriptProcessorNode: PropTypes.object,
  volumeGainNode: PropTypes.object,
  analyser: PropTypes.object,
  meterCount: PropTypes.number,
  startRecording: PropTypes.func,
  startRecordingLabel: PropTypes.string
});

_defineProperty(RecorderVolumeMeter, "defaultProps", {
  id: '',
  analyser: null,
  scriptProcessorNode: null,
  volumeGainNode: null,
  startRecording: function startRecording() {},
  meterCount: 12,
  startRecordingLabel: 'TRANSLATE:START_RECORDING'
});

var PROGRESS_WAVE = 'progress-wave';
export var RecorderProcessor =
/*#__PURE__*/
function (_React$Component2) {
  _inheritsLoose(RecorderProcessor, _React$Component2);

  function RecorderProcessor(props) {
    var _this3;

    _this3 = _React$Component2.call(this, props) || this;
    _this3.state = {
      inputVolume: 0,
      currentTime: 0
    };
    _this3.initTime = 0;
    _this3.data = [];
    return _this3;
  }

  var _proto2 = RecorderProcessor.prototype;

  _proto2.componentWillMount = function componentWillMount() {
    var _this4 = this;

    var _this$props = this.props,
        recorder = _this$props.recorder,
        scriptProcessorNode = _this$props.scriptProcessorNode,
        analyser = _this$props.analyser,
        stopRecording = _this$props.stopRecording,
        maxRecordTime = _this$props.maxRecordTime;

    scriptProcessorNode.onaudioprocess = function (e) {
      recorder.onaudioprocess(e);

      if (!recorder.recording) {
        return;
      }

      var inputVolume = _this4.state.inputVolume;
      var currentTime = e.playbackTime;

      if (!_this4.initTime) {
        _this4.initTime = currentTime;
      }

      currentTime -= _this4.initTime;

      if (currentTime < maxRecordTime) {
        inputVolume = getRecorderInputVolume(analyser, inputVolume, false);

        _this4.data.push(inputVolume);

        if (_this4.maxMeterCount && _this4.data.length > _this4.maxMeterCount) {
          _this4.data.shift();
        }

        if (!_this4.unmounted) {
          _this4.setState({
            inputVolume: inputVolume,
            currentTime: currentTime
          });
        }
      } else {
        if (_this4.unmounted) {
          stopRecording();
        }
      }
    };

    recorder.record();
  };

  _proto2.componentDidMount = function componentDidMount() {
    var progressWaveLength = this.refs[PROGRESS_WAVE].offsetWidth;
    this.maxMeterCount = (progressWaveLength + 1) / 2;
  };

  _proto2.componentWillUnmount = function componentWillUnmount() {
    this.unmounted = true;
  };

  _proto2.getStopRecordButton = function getStopRecordButton() {
    var _this5 = this;

    return React.createElement(Button, {
      "aria-label": this.props.stopRecordingLabel,
      className: "rc-audio-icon-wrapper rc-record-complete",
      testAutomationId: "audioStopRecord" + this.props.id,
      onClick: function onClick() {
        _this5.props.stopRecording();
      }
    }, React.createElement(Icon, {
      name: "record-complete"
    }));
  };

  _proto2.getWave = function getWave() {
    var _this$props2 = this.props,
        meterMaxHeight = _this$props2.meterMaxHeight,
        meterMinHeight = _this$props2.meterMinHeight;

    var progressedMeter = _.map(this.data, function (volume, index) {
      var height = meterMinHeight + Math.min(volume, METER_FULL_VALUE) / METER_FULL_VALUE * (meterMaxHeight - meterMinHeight);
      return React.createElement("div", {
        className: "progress-volume-meter",
        style: {
          left: index * 2 + 'px',
          height: height + 'px'
        },
        key: "progress-volume-meter-unit-" + index
      });
    });

    return React.createElement("div", {
      className: "progress-wav",
      ref: PROGRESS_WAVE
    }, progressedMeter);
  };

  _proto2.render = function render() {
    var currentTime = this.state.currentTime;
    return React.createElement("div", {
      className: "rc-audio-recorder recorder-progressor"
    }, this.getStopRecordButton(), this.getWave(), React.createElement("div", {
      className: "audio-duration"
    }, formatSeconds(currentTime), " / ", formatSeconds(this.props.maxRecordTime)));
  };

  return RecorderProcessor;
}(React.Component);

_defineProperty(RecorderProcessor, "propTypes", {
  id: PropTypes.string,
  analyser: PropTypes.object,
  recorder: PropTypes.object,
  scriptProcessorNode: PropTypes.object,
  meterMinHeight: PropTypes.number,
  meterMaxHeight: PropTypes.number,
  maxRecordTime: PropTypes.number,
  stopRecording: PropTypes.func,
  stopRecordingLabel: PropTypes.string
});

_defineProperty(RecorderProcessor, "defaultProps", {
  id: '',
  analyser: null,
  recorder: null,
  scriptProcessorNode: null,
  stopRecording: function stopRecording() {},
  meterMaxHeight: 15,
  meterMinHeight: 5,
  maxRecordTime: 300,
  //5min
  stopRecordingLabel: 'TRANSLATE:STOP_RECORDING'
});

var STEPS = {
  METER: 0,
  RECORDING: 1,
  RECORDED: 2
};
export var AudioRecorder =
/*#__PURE__*/
function (_React$Component3) {
  _inheritsLoose(AudioRecorder, _React$Component3);

  function AudioRecorder(props) {
    var _this6;

    _this6 = _React$Component3.call(this, props) || this;
    _this6.state = {
      step: STEPS.METER,
      ready: false
    };
    return _this6;
  }

  var _proto3 = AudioRecorder.prototype;

  _proto3.componentWillMount = function componentWillMount() {
    var _this7 = this;

    if (audioRecorderSupported()) {
      var _this$props3 = this.props,
          audioRecordError = _this$props3.audioRecordError,
          deviceNotFoundError = _this$props3.deviceNotFoundError;
      window.audioContext = window.audioContext || new AudioContext();
      this.audioContext = window.audioContext;
      navigator.mediaDevices.getUserMedia({
        audio: true
      }).then(function (stream) {
        _this7.localStream = stream;
        var config = _this7.props.config;
        _this7.mediaStream = _this7.audioContext.createMediaStreamSource(stream);
        _this7.volumeGainNode = _this7.audioContext.createGain();

        _this7.mediaStream.connect(_this7.volumeGainNode);

        _this7.analyser = _this7.audioContext.createAnalyser();
        _this7.analyser.fftSize = config.bufferLen / config.numChannels;

        _this7.volumeGainNode.connect(_this7.analyser);

        _this7.scriptProcessorNode = _this7.audioContext.createScriptProcessor(config.bufferLen, config.numChannels, config.numChannels);

        _this7.analyser.connect(_this7.scriptProcessorNode);

        _this7.scriptProcessorNode.connect(_this7.audioContext.destination); //this should not be necessary


        _this7.setState({
          ready: true
        });
      }).catch(function (e) {
        if (e.name === 'DevicesNotFoundError' || //for Chrome
        e.name === 'NotFoundError') {
          //for firefox
          _this7.errorText = deviceNotFoundError;
        } else {
          _this7.errorText = audioRecordError || e.name;
        } // eslint-disable-next-line


        console.log(e);

        _this7.setState({
          ready: true
        });
      });
    }
  };

  _proto3.componentWillUnmount = function componentWillUnmount() {
    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;

      _.forEach(this.localStream.getTracks(), function (track) {
        track.stop();
      });
    }
  };

  _proto3.getText = function getText(title, text) {
    var titleCmp = title ? React.createElement(Block, {
      condensed: true
    }, React.createElement("strong", null, title)) : null;
    return React.createElement("div", null, titleCmp, React.createElement(Block, null, React.createElement(DangerousHTML, null, text)));
  };

  _proto3.getUploadButton = function getUploadButton() {
    var _this8 = this;

    return this.state.step === STEPS.RECORDED ? React.createElement("div", {
      className: "rc-audio-upload"
    }, React.createElement("a", {
      role: "button",
      "aria-label": this.props.uploadLabel,
      href: "#",
      onClick: function onClick(e) {
        e.preventDefault();

        _this8.uploadAudio();
      }
    }, React.createElement(Icon, {
      name: "circle-upload"
    }))) : null;
  };

  _proto3.createRecorder = function createRecorder() {
    return new RecorderLib(this.props.config, this.audioContext.sampleRate);
  };

  _proto3.startRecording = function startRecording() {
    this.recorder = this.recorder || this.createRecorder();
    this.setState({
      step: STEPS.RECORDING
    });
  };

  _proto3.stopRecording = function stopRecording() {
    var _this9 = this;

    if (this.recorder) {
      this.recorder.stop();
      this.recorder.exportWAV(function (audioBlobData) {
        var onRecordingStopped = _this9.props.onRecordingStopped;

        if (onRecordingStopped) {
          onRecordingStopped(audioBlobData);
        }

        _this9.audioBlobData = audioBlobData;

        _this9.recorder.clear();

        _this9.setState({
          step: STEPS.RECORDED
        });
      });
    }
  };

  _proto3.uploadAudio = function uploadAudio() {
    if (this.audioBlobData) {
      var _this$props4 = this.props,
          exportAudio = _this$props4.exportAudio,
          fileName = _this$props4.fileName;
      var formData = new FormData();
      formData.append('file', this.audioBlobData, fileName);

      if (exportAudio) {
        exportAudio(formData);
      }
    }
  };

  _proto3.render = function render() {
    var _this10 = this;

    var _this$props5 = this.props,
        unsupportText = _this$props5.unsupportText,
        volumeMeterTitle = _this$props5.volumeMeterTitle,
        volumeMeterText = _this$props5.volumeMeterText,
        processorTitle = _this$props5.processorTitle,
        processorText = _this$props5.processorText,
        completedTitle = _this$props5.completedTitle,
        completedText = _this$props5.completedText;
    var _this$state3 = this.state,
        ready = _this$state3.ready,
        step = _this$state3.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.createElement(RecorderVolumeMeter, {
          scriptProcessorNode: this.scriptProcessorNode,
          volumeGainNode: this.volumeGainNode,
          analyser: this.analyser,
          startRecording: function startRecording() {
            _this10.startRecording();
          }
        });
        break;

      case STEPS.RECORDING:
        promptText = this.getText(processorTitle, processorText);
        recorderStep = React.createElement(RecorderProcessor, {
          scriptProcessorNode: this.scriptProcessorNode,
          analyser: this.analyser,
          recorder: this.recorder,
          stopRecording: function stopRecording() {
            _this10.stopRecording();
          }
        });
        break;

      case STEPS.RECORDED:
        promptText = this.getText(completedTitle, completedText);
        var audioUrl = this.audioBlobData ? URL.createObjectURL(this.audioBlobData) : '';
        recorderStep = audioUrl ? React.createElement(AudioPlayer, {
          audioUrl: audioUrl,
          onRecord: function onRecord() {
            _this10.setState({
              step: STEPS.METER
            });
          }
        }) : null;
        break;

      default:
        break;
    }

    return React.createElement("div", null, promptText, React.createElement("div", {
      className: "rc-audio"
    }, recorderStep, this.getUploadButton()));
  };

  return AudioRecorder;
}(React.Component);

_defineProperty(AudioRecorder, "propTypes", {
  unsupportText: PropTypes.string,
  volumeMeterTitle: PropTypes.string,
  volumeMeterText: PropTypes.string,
  processorTitle: PropTypes.string,
  processorText: PropTypes.string,
  completedTitle: PropTypes.string,
  completedText: PropTypes.string,
  fileName: PropTypes.string,
  audioRecordError: PropTypes.string,
  deviceNotFoundError: PropTypes.string,
  config: PropTypes.object,
  exportAudio: PropTypes.func,
  onRecordingStopped: PropTypes.func,
  uploadLabel: PropTypes.string
});

_defineProperty(AudioRecorder, "defaultProps", {
  exportAudio: null,
  onRecordingStopped: null,
  config: {
    bufferLen: 2048,
    //4096,
    numChannels: 1,
    //from 2 to 1, to reduce audio size
    mimeType: 'audio/wav'
  },
  fileName: 'audio.wav',
  id: 'audio-recorder',
  unsupportText: 'TRANSLATE:MICROPHONE_NOT_SUPPORTED',
  volumeMeterTitle: 'TRANSLATE:MICROPHONE_TEST',
  volumeMeterText: 'TRANSLATE:MICROPHONE_TEST_TEXT',
  processorTitle: 'TRANSLATE:RECORDING',
  processorText: 'TRANSLATE:RECORDING_IN_PROGRESS',
  completedTitle: 'TRANSLATE:RECORDING',
  completedText: 'TRANSLATE:MICROPHONE_RECORD_COMPLETE',
  deviceNotFoundError: 'TRANSLATE:MICROPHONE_NOT_FOUND_ERROR',
  audioRecordError: 'TRANSLATE:MICROPHONE_RECORD_ERROR',
  uploadLabel: 'TRANSLATE:UPLOAD'
});

export function audioRecorderSupported() {
  window.AudioContext = window.AudioContext || window.webkitAudioContext;
  window.URL = window.URL || window.webkitURL;
  return window.AudioContext && window.URL && navigator.mediaDevices && navigator.mediaDevices.getUserMedia;
}