import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from '../../../actions/compose';
import { getPointerPosition } from '../../video';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import IconButton from 'mastodon/components/icon_button';
import Button from 'mastodon/components/button';
import Video from 'mastodon/features/video';
import Audio from 'mastodon/features/audio';
import Textarea from 'react-textarea-autosize';
import UploadProgress from 'mastodon/features/compose/components/upload_progress';
import CharacterCounter from 'mastodon/features/compose/components/character_counter';
import { length } from 'stringz';
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
import GIFV from 'mastodon/components/gifv';
import { me } from 'mastodon/initial_state';
// eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
// eslint-disable-next-line import/extensions
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
import { assetHost } from 'mastodon/utils/config';
const messages = defineMessages({
  close: { id: 'lightbox.close', defaultMessage: 'Close' },
  apply: { id: 'upload_modal.apply', defaultMessage: 'Apply' },
  applying: { id: 'upload_modal.applying', defaultMessage: 'Applying…' },
  placeholder: { id: 'upload_modal.description_placeholder', defaultMessage: 'A quick brown fox jumps over the lazy dog' },
  chooseImage: { id: 'upload_modal.choose_image', defaultMessage: 'Choose image' },
  discardMessage: { id: 'confirmations.discard_edit_media.message', defaultMessage: 'You have unsaved changes to the media description or preview, discard them anyway?' },
  discardConfirm: { id: 'confirmations.discard_edit_media.confirm', defaultMessage: 'Discard' },
});
const mapStateToProps = (state, { id }) => ({
  media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
  account: state.getIn(['accounts', me]),
  isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']),
  description: state.getIn(['compose', 'media_modal', 'description']),
  lang: state.getIn(['compose', 'language']),
  focusX: state.getIn(['compose', 'media_modal', 'focusX']),
  focusY: state.getIn(['compose', 'media_modal', 'focusY']),
  dirty: state.getIn(['compose', 'media_modal', 'dirty']),
  is_changing_upload: state.getIn(['compose', 'is_changing_upload']),
});
const mapDispatchToProps = (dispatch, { id }) => ({
  onSave: (description, x, y) => {
    dispatch(changeUploadCompose(id, { description, focus: `${x.toFixed(2)},${y.toFixed(2)}` }));
  },
  onChangeDescription: (description) => {
    dispatch(onChangeMediaDescription(description));
  },
  onChangeFocus: (focusX, focusY) => {
    dispatch(onChangeMediaFocus(focusX, focusY));
  },
  onSelectThumbnail: files => {
    dispatch(uploadThumbnail(id, files[0]));
  },
});
const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******')
  .replace(/\n/g, ' ')
  .replace(/\*\*\*\*\*\*/g, '\n\n');
class ImageLoader extends React.PureComponent {
  static propTypes = {
    src: PropTypes.string.isRequired,
    width: PropTypes.number,
    height: PropTypes.number,
  };
  state = {
    loading: true,
  };
  componentDidMount() {
    const image = new Image();
    image.addEventListener('load', () => this.setState({ loading: false }));
    image.src = this.props.src;
  }
  render () {
    const { loading } = this.state;
    if (loading) {
      return ;
    } else {
      return ;
    }
  }
}
class FocalPointModal extends ImmutablePureComponent {
  static propTypes = {
    media: ImmutablePropTypes.map.isRequired,
    account: ImmutablePropTypes.map.isRequired,
    isUploadingThumbnail: PropTypes.bool,
    onSave: PropTypes.func.isRequired,
    onChangeDescription: PropTypes.func.isRequired,
    onChangeFocus: PropTypes.func.isRequired,
    onSelectThumbnail: PropTypes.func.isRequired,
    onClose: PropTypes.func.isRequired,
    intl: PropTypes.object.isRequired,
  };
  state = {
    dragging: false,
    dirty: false,
    progress: 0,
    loading: true,
    ocrStatus: '',
  };
  componentWillUnmount () {
    document.removeEventListener('mousemove', this.handleMouseMove);
    document.removeEventListener('mouseup', this.handleMouseUp);
  }
  handleMouseDown = e => {
    document.addEventListener('mousemove', this.handleMouseMove);
    document.addEventListener('mouseup', this.handleMouseUp);
    this.updatePosition(e);
    this.setState({ dragging: true });
  };
  handleTouchStart = e => {
    document.addEventListener('touchmove', this.handleMouseMove);
    document.addEventListener('touchend', this.handleTouchEnd);
    this.updatePosition(e);
    this.setState({ dragging: true });
  };
  handleMouseMove = e => {
    this.updatePosition(e);
  };
  handleMouseUp = () => {
    document.removeEventListener('mousemove', this.handleMouseMove);
    document.removeEventListener('mouseup', this.handleMouseUp);
    this.setState({ dragging: false });
  };
  handleTouchEnd = () => {
    document.removeEventListener('touchmove', this.handleMouseMove);
    document.removeEventListener('touchend', this.handleTouchEnd);
    this.setState({ dragging: false });
  };
  updatePosition = e => {
    const { x, y } = getPointerPosition(this.node, e);
    const focusX   = (x - .5) *  2;
    const focusY   = (y - .5) * -2;
    this.props.onChangeFocus(focusX, focusY);
  };
  handleChange = e => {
    this.props.onChangeDescription(e.target.value);
  };
  handleKeyDown = (e) => {
    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
      this.props.onChangeDescription(e.target.value);
      this.handleSubmit(e);
    }
  };
  handleSubmit = (e) => {
    e.preventDefault();
    e.stopPropagation();
    this.props.onSave(this.props.description, this.props.focusX, this.props.focusY);
  };
  getCloseConfirmationMessage = () => {
    const { intl, dirty } = this.props;
    if (dirty) {
      return {
        message: intl.formatMessage(messages.discardMessage),
        confirm: intl.formatMessage(messages.discardConfirm),
      };
    } else {
      return null;
    }
  };
  setRef = c => {
    this.node = c;
  };
  handleTextDetection = () => {
    this._detectText();
  };
  _detectText = (refreshCache = false) => {
    const { media } = this.props;
    this.setState({ detecting: true });
    fetchTesseract().then(({ createWorker }) => {
      const worker = createWorker({
        workerPath: tesseractWorkerPath,
        corePath: tesseractCorePath,
        langPath: `${assetHost}/ocr/lang-data/`,
        logger: ({ status, progress }) => {
          if (status === 'recognizing text') {
            this.setState({ ocrStatus: 'detecting', progress });
          } else {
            this.setState({ ocrStatus: 'preparing', progress });
          }
        },
        cacheMethod: refreshCache ? 'refresh' : 'write',
      });
      let media_url = media.get('url');
      if (window.URL && URL.createObjectURL) {
        try {
          media_url = URL.createObjectURL(media.get('file'));
        } catch (error) {
          console.error(error);
        }
      }
      return (async () => {
        await worker.load();
        await worker.loadLanguage('eng');
        await worker.initialize('eng');
        const { data: { text } } = await worker.recognize(media_url);
        this.setState({ detecting: false });
        this.props.onChangeDescription(removeExtraLineBreaks(text));
        await worker.terminate();
      })().catch((e) => {
        if (refreshCache) {
          throw e;
        } else {
          this._detectText(true);
        }
      });
    }).catch((e) => {
      console.error(e);
      this.setState({ detecting: false });
    });
  };
  handleThumbnailChange = e => {
    if (e.target.files.length > 0) {
      this.props.onSelectThumbnail(e.target.files);
    }
  };
  setFileInputRef = c => {
    this.fileInput = c;
  };
  handleFileInputClick = () => {
    this.fileInput.click();
  };
  render () {
    const { media, intl, account, onClose, isUploadingThumbnail, description, lang, focusX, focusY, dirty, is_changing_upload } = this.props;
    const { dragging, detecting, progress, ocrStatus } = this.state;
    const x = (focusX /  2) + .5;
    const y = (focusY / -2) + .5;
    const width  = media.getIn(['meta', 'original', 'width']) || null;
    const height = media.getIn(['meta', 'original', 'height']) || null;
    const focals = ['image', 'gifv'].includes(media.get('type'));
    const thumbnailable = ['audio', 'video'].includes(media.get('type'));
    const previewRatio  = 16/9;
    const previewWidth  = 200;
    const previewHeight = previewWidth / previewRatio;
    let descriptionLabel = null;
    if (media.get('type') === 'audio') {
      descriptionLabel =