import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  CompositeDecorator,
  Editor,
  EditorState,
  Entity,
  RichUtils,
  DefaultDraftBlockRenderMap
} from 'draft-js';
import classNames from 'classnames';
import { stateToHTML } from 'draft-js-export-html';
import { stateFromHTML } from 'draft-js-import-html';

import FieldControl from '../FieldControl';
import BlockTypesControls from './BlockTypesControls';
import InlineStylesControls from './InlineStylesControls';
import LinkButton from './LinkButton/index';
import { getInputProps } from '../_helpers/tools';
import './rich-text-editor.scss';

const draftBlockRender = DefaultDraftBlockRenderMap
  .remove('header-one')
  .remove('header-two')
  .remove('header-three')
  .remove('header-four')
  .remove('header-five')
  .remove('header-six')
  .remove('atomic')
  .remove('code-block')
  .remove('blockquote')
  .concat({
    'text-align-left': { element: 'div' },
    'text-align-right': { element: 'div' },
    'text-align-center': { element: 'div' },
    'text-align-justify': { element: 'div' }
  });

function filterClassName(type) {
  switch (type) {
    case 'text-align-left':
    case 'text-align-right':
    case 'text-align-center':
    case 'text-align-justify':
      return type;
    default:
      return undefined;
  }
}
function myBlockStyleFn(contentBlock) {
  const type = contentBlock.getType();
  return filterClassName(type);
}
const optionsStateToHTML = {
  blockStyleFn: block => {
    const className = myBlockStyleFn(block);
    return className
      ? {
        attributes: { class: className }
      } : {};
  }
};
const optionsStateFromHTML = {
  customBlockFn: element => {
    const { className } = element;
    const type = filterClassName(className);
    if (type) {
      return { type, element: 'div' };
    }
  }
};

const Link = props => {
  const { url } = props.contentState.getEntity(props.entityKey).getData();
  return (
    <a href={url}>
      {props.children}
    </a>
  );
};
Link.propTypes = {
  entityKey: PropTypes.string,
  children: PropTypes.node,
  contentState: PropTypes.object.isRequired
};

Link.defaultProps = {
  entityKey: null,
  children: null
};

function findLinkEntities(contentBlock, callback) {
  contentBlock.findEntityRanges(
    character => {
      const entityKey = character.getEntity();
      return (
        entityKey !== null
        && Entity.get(entityKey).getType() === 'LINK'
      );
    },
    callback
  );
}

class RichTextEditor extends Component {

  constructor(props) {
    super(props);
    this.state = { editorState: EditorState.createEmpty() };
  }

  componentDidMount() {
    this.setEditorContent();
  }

  componentWillReceiveProps(nextProps) {
    const { value } = getInputProps(this.props);
    const { value: valueNextProps } = getInputProps(nextProps);
    if (value === '' && valueNextProps !== '') {
      const contentState = stateFromHTML(valueNextProps, optionsStateFromHTML);
      // this uses the DOM, so it must be restricted to the CLIENT
      this.state = {
        editorState: EditorState.push(this.state.editorState, contentState, 'insert-fragment')
      };
    } else if (valueNextProps === '') {
      const contentState = stateFromHTML(valueNextProps, optionsStateFromHTML);
      // this uses the DOM, so it must be restricted to the CLIENT
      this.setState({
        editorState: EditorState.push(this.state.editorState, contentState, 'insert-fragment')
      });
    }
  }

  onChange = editorState => {
    const { onChange } = getInputProps(this.props);
    const html = editorState.getCurrentContent().hasText()
      ? stateToHTML(editorState.getCurrentContent(), optionsStateToHTML) : '';
    this.setState({ editorState });
    onChange(html);
  };

  setEditorContent = () => {
    const decorator = new CompositeDecorator([
      {
        strategy: findLinkEntities,
        component: Link
      }
    ]);
    const value = getInputProps(this.props).value || '<p></p>';
    const initialContentState = stateFromHTML(value, optionsStateFromHTML);
    // this uses the DOM, so it must be restricted to the CLIENT
    this.setState({ editorState: EditorState.createWithContent(initialContentState, decorator) });
  }

  handleKeyCommand = command => {
    const { editorState } = this.state;
    const newState = RichUtils.handleKeyCommand(editorState, command);
    if (newState) {
      this.onChange(newState);
      return true;
    }
    return false;
  };
  toggleBlockType = blockType => {
    this.onChange(
      RichUtils.toggleBlockType(
        this.state.editorState,
        blockType
      )
    );
  };
  toggleInlineStyle = inlineStyle => {
    this.onChange(
      RichUtils.toggleInlineStyle(
        this.state.editorState,
        inlineStyle
      )
    );
  };
  focus = () => {
    this.editor.focus();
  };


  render() {
    const { className, editorClassName, disabled, ...props } = this.props;
    const { value } = getInputProps(props);
    const { editorState } = this.state;
    return (
      <FieldControl {...props}>
        <div
          className={classNames(
            'rich-text-editor', className, { 'rich-text-editor--disabled': disabled }
          )}
        >
          <div className="rich-text-editor--controls">
            <InlineStylesControls
              editorState={editorState}
              onToggle={this.toggleInlineStyle}
              controls={[
                { label: 'B', style: 'BOLD', className: 'bold' },
                { label: 'I', style: 'ITALIC', className: 'italic' },
                { label: 'U', style: 'UNDERLINE', className: 'underline' }
              ]}
            />
            <LinkButton className="link" editorState={editorState} label="" onChange={this.onChange} />
            <BlockTypesControls
              editorState={editorState}
              onToggle={this.toggleBlockType}
              controls={[
                { label: '', style: 'unordered-list-item', className: 'unordered-list' },
                { label: '', style: 'ordered-list-item', className: 'ordered-list' }
              ]}
            />
            <BlockTypesControls
              editorState={editorState}
              onToggle={this.toggleBlockType}
              controls={[
                { label: '', style: 'text-align-left', className: 'align-left' },
                { label: '', style: 'text-align-center', className: 'align-center' },
                { label: '', style: 'text-align-right', className: 'align-right' },
                { label: '', style: 'text-align-justify', className: 'align-justify' }
              ]}
            />
          </div>
          <div className={classNames(editorClassName, 'editor-wrapper')}>
            {disabled ? <div dangerouslySetInnerHTML={{ __html: value }} />
              : <Editor
                ref={ref => { this.editor = ref; }}
                editorState={editorState}
                onChange={this.onChange}
                handleKeyCommand={this.handleKeyCommand}
                blockRenderMap={draftBlockRender}
                blockStyleFn={myBlockStyleFn}
              />
            }
          </div>
        </div>
      </FieldControl>
    );
  }
}

RichTextEditor.propTypes = {
  value: PropTypes.string,
  onChange: PropTypes.func,
  className: PropTypes.string,
  editorClassName: PropTypes.string,
  disabled: PropTypes.bool
};

RichTextEditor.defaultProps = {
  value: '',
  onChange: null,
  className: undefined,
  editorClassName: undefined,
  disabled: false
};

export default RichTextEditor;
