import { Editor } from '@tinymce/tinymce-react';
import axios from 'axios';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import tinymce from 'tinymce/tinymce';
import { getFileByEventId, uploadFile } from '../../../api/icat/icatPlus';
import UI from '../../../config/ui/config';
import {
  EDIT_EVENT_CONTEXT,
  HTML_CONTENT_FORMAT,
  LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_HTML_FORMAT,
  LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_PLAINTEXT_FORMAT,
  LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_HTML_FORMAT,
  LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_PLAINTEXT_FORMAT,
  NEW_EVENT_CONTEXT,
  PLAINTEXT_CONTENT_FORMAT,
} from '../../../constants/eventTypes';
import { getContent } from '../../../helpers/eventHelpers';
import { EditionModeConfig } from './tinymceConfig';

const EDITOR_ID_FOR_CREATION = 'myEditorForCreation';
const EDITOR_ID_FOR_EDITION = 'myEditorForEdition';

export default class EditorWrapper extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: props.event
        ? getContent(props.event.content, HTML_CONTENT_FORMAT)
        : undefined, // HTML text
    };

    this.onEditorStateChanged = this.onEditorStateChanged.bind(this);
    this.setImageHeightByCSSRule = this.setImageHeightByCSSRule.bind(this);
    this.imagesUploadHandler = this.imagesUploadHandler.bind(this);
  }

  render() {
    const config = new EditionModeConfig(window.innerWidth);

    return (
      <Editor
        id={this.props.event ? EDITOR_ID_FOR_EDITION : EDITOR_ID_FOR_CREATION}
        init={{
          plugins: config.plugins,
          skin_url: config.skin_url,
          branding: config.branding,
          readonly: config.readonly,
          toolbar: config.toolbar,
          menubar: config.menubar,
          statusbar: config.statusbar,
          images_upload_handler: this.imagesUploadHandler,
          paste_data_images: config.paste_data_images,
          formats: config.formats,
          //content_css: config.content_css,
        }}
        value={this.state.text}
        onEditorChange={this.onEditorStateChanged}
      />
    );
  }

  /**
   * Function triggered when the editor state changes (mouse click, key press for example )
   * @param {String} text current editor content in HTML format
   */
  onEditorStateChanged(text) {
    const context = this.props.event ? EDIT_EVENT_CONTEXT : NEW_EVENT_CONTEXT;
    if (text) {
      // Editor content has been modified by the user. Save the update to localStorage
      this.storeToLocalStorage(
        context,
        tinymce.activeEditor.getContent({ format: 'text' }),
        PLAINTEXT_CONTENT_FORMAT
      );
      this.storeToLocalStorage(
        context,
        tinymce.activeEditor.getContent(),
        HTML_CONTENT_FORMAT
      );
    }

    this.setImageHeightByCSSRule();
    this.props.onContentChanged();

    this.setState({ text });
  }

  componentDidMount() {
    // Make sure localstorage is populated in case the editor is not clicked on at all
    const context = this.props.event ? EDIT_EVENT_CONTEXT : NEW_EVENT_CONTEXT;
    this.storeToLocalStorage(context, this.state.text, HTML_CONTENT_FORMAT);
  }

  componentWillUnmount() {
    const context = this.props.event ? EDIT_EVENT_CONTEXT : NEW_EVENT_CONTEXT;
    if (context === NEW_EVENT_CONTEXT) {
      localStorage.removeItem(
        LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_PLAINTEXT_FORMAT
      );
      localStorage.removeItem(
        LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_HTML_FORMAT
      );
    }

    if (context === EDIT_EVENT_CONTEXT) {
      localStorage.removeItem(
        LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_PLAINTEXT_FORMAT +
          this.props.event._id
      );
      localStorage.removeItem(
        LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_HTML_FORMAT +
          this.props.event._id
      );
    }
  }

  /**
   * Store data to the localstorage
   * @param {string} context
   * @param {string} content content data to store
   * @param {string} contentFormat data format
   */
  storeToLocalStorage(context, content, contentFormat) {
    if (context && contentFormat) {
      if (context === NEW_EVENT_CONTEXT) {
        if (contentFormat === PLAINTEXT_CONTENT_FORMAT) {
          localStorage.setItem(
            LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_PLAINTEXT_FORMAT,
            content
          );
        } else if (contentFormat === HTML_CONTENT_FORMAT) {
          localStorage.setItem(
            LOCALSTORAGE_KEY_NEW_EVENT_CONTENT_IN_HTML_FORMAT,
            content
          );
        }
      }
      if (context === EDIT_EVENT_CONTEXT) {
        if (contentFormat === PLAINTEXT_CONTENT_FORMAT) {
          localStorage.setItem(
            LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_PLAINTEXT_FORMAT +
              this.props.event._id,
            content
          );
        } else if (contentFormat === HTML_CONTENT_FORMAT) {
          localStorage.setItem(
            LOCALSTORAGE_KEY_EDITED_EVENT_CONTENT_IN_HTML_FORMAT +
              this.props.event._id,
            content
          );
        }
      }
    }
  }

  /**
   * Sets a default image height using a CSS rule. This is a trick to make sure the image height
   * is applied in the editor (for scrollabar propose) especially because the image is not necessary
   * yet downloaded at this step. The height is changed to auto after the image is fully downloaded
   * such that image ratio is kept.
   */
  setImageHeightByCSSRule() {
    if (tinymce && tinymce.activeEditor) {
      const selectedNode = tinymce.activeEditor.selection.getNode();
      if (
        selectedNode.nodeName === 'IMG' &&
        selectedNode.style.height !== 'auto'
      ) {
        const nxElement = selectedNode;
        nxElement.style.height = UI.logbook.NEW_EVENT_MAX_HEIGHT;
        nxElement.style.width = 'auto'; // a css trick for some browsers IE8 and old iceweasel
        nxElement.onload = this.onImageLoaded;
        tinymce.activeEditor.dom.replace(nxElement, selectedNode);
      }
    }
  }

  /**
   * Defines what to do when the user drag an image onto the dropzone of the editor image plugin. This function must return a promise.
   * The value of a fullfilled promise must be an array of the form { data: { link: url } } where url value is the link to the image which
   * has just been uoloaded to the ICAT+ server.
   * @param {*} file : the image which has just been dropped on the drop zone.
   */

  imagesUploadHandler(blobInfo, success, failure) {
    const { investigationId } = this.props;
    const sessionId = this.props.user.sessionId;

    const data = new FormData();
    data.append('file', blobInfo.blob(), blobInfo.filename());
    data.append('investigationId', investigationId);
    data.append('creationDate', Date());
    data.append('type', 'attachment');
    data.append('category', 'file');
    data.append('username', this.props.user.username);

    axios({
      method: 'post',
      url: uploadFile(sessionId, investigationId),
      data,
    }).then(
      (value) => {
        const eventId = value.data._id;
        success(getFileByEventId(sessionId, investigationId, eventId));
      },
      (error) => {
        console.log(
          '[ERROR] Retrieval of the image you have just upladed into the editor failed ! '
        );
        failure(error);
      }
    );
  }
}

EditorWrapper.propTypes = {
  /** Event being edited. When event is undefined, editor is used to create content of a new event */
  event: PropTypes.object,
  /** the investigationId of the event being edited. */
  investigationId: PropTypes.string,
  /** Function triggered when editor content changed */
  onContentChanged: PropTypes.func,
  /** the user who is currently logged in */
  user: PropTypes.object.isRequired,
};
