import React from 'react';
import {Trans, withNamespaces} from 'react-i18next';

import DraftEditor from 'draft-js-plugins-editor';
import {
    EditorState,
    RichUtils,
    convertFromRaw,
    convertToRaw,
    EditorBlock
} from 'draft-js';
import {draftToMarkdown, markdownToDraft} from 'markdown-draft-js';
import {stateToHTML} from 'draft-js-export-html';
import {stateFromHTML} from 'draft-js-import-html';
import createMarkdownShortcutsPlugin from 'draft-js-markdown-shortcuts-plugin';
//import createEmojiPlugin from 'draft-js-emoji-plugin';

import '../style/editor.scss';
import {ReactComponent as MarkdownLogo} from '../asset/icon/markdown.svg';

import {
    MdFormatQuote,
    MdFormatListBulleted,
    MdFormatListNumbered,
    //MdInsertLink,
    MdCode,
    MdFormatBold,
    MdFormatItalic,
    MdFormatUnderlined
} from 'react-icons/md';

import {withTheme, injectSheet} from '../lib/theme';
import StyleGrid from "../lib/StyleGrid";

// todo: check emoji performance issues
// currently it seems that with emoji turned on we got serious performance issues, measuring 2-3s more on initial load
const turnOnEmoji = false;

const styling = theme => ({
    wrapper: {
        display: 'flex',
        flexDirection: 'column',
        width: '100%',
        //padding: theme.gutter.base * 2 + 'px 0',
        //margin: theme.gutter.base * 2 + 'px 0'
    },
    wrapperControls: {
        display: 'flex',
        flexWrap: 'wrap',
        margin: '2px auto'
    },
    separator: {
        display: 'none',
        width: '1px',
        margin: '0 ' + theme.gutter.base * 2 + 'px',
        [StyleGrid.smUp]: {
            display: 'block',
        }
    },
    controls: {
        display: 'flex',
        justifyContent: 'center',
        flexWrap: 'wrap',
    },
    edited: {
        display: 'flex',
        paddingLeft: '6px'
    },
    editor: {
        position: 'relative'
    },
    editorNoPlaceholder: {},
    markdown: {
        position: 'absolute',
        right: '6px',
        zIndex: 1,
    },
    markdownTooltip: {
        position: 'absolute',
        zIndex: 3,
        opacity: 0,
        margin: 0,
        transform: 'translate(calc(-100% - 6px), -100%)',
        minWidth: '160px',
        maxWidth: '320px',
        padding: theme.gutter.base * 2 + 'px',
        fontSize: '0.875rem',
        pointerEvents: 'none',
        '&.open': {
            pointerEvents: 'all',
            opacity: 1
        }
    }
});

//
// Button

const stylebtn = {
    transition: '0.2s linear background',
    cursor: 'pointer',
    padding: '2px',
    margin: '0 2px',
    fontSize: '0.875rem',
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center'
};
const stylebtnActive = theme => ({
    background: theme.btnActive,
    transition: '0.2s linear background',
    cursor: 'pointer',
    padding: '2px',
    margin: '0 2px',
    fontSize: '0.875rem',
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center'
});

function getBlockStyle(block) {
    switch(block.getType()) {
        case 'blockquote':
            return 'RichEditor-blockquote';
        default:
            return null;
    }
}

class StyleButton extends React.Component {

    constructor(props) {
        super(props);
        this.onToggle = (e) => {
            e.preventDefault();
            this.props.onToggle(this.props.style);
        };
    }

    render() {
        const {label, theme, active} = this.props;

        return (
            <span style={(active ? stylebtnActive(theme) : stylebtn)} onMouseDown={this.onToggle}>{label}</span>
        );
    }
}

const Button = withTheme(StyleButton);

// Button
//

//
// Types Definition

// Custom overrides for "code" style.
const styleMap = {
    CODE: {
        backgroundColor: 'rgba(0, 0, 0, 0.3)',
        fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
        fontSize: 16,
        padding: 3,
    },
};

const BLOCK_TYPES = [
    {key: 1, label: 'H1', style: 'header-one'},
    {key: 2, label: 'H2', style: 'header-two'},
    {key: 3, label: 'H3', style: 'header-three'},
    {key: 4, label: 'H4', style: 'header-four'},
    {key: 5, label: 'H5', style: 'header-five'},
    {key: 6, label: 'H6', style: 'header-six'},
    {key: 7, label: <MdFormatQuote size={'1.25em'}/>, style: 'blockquote'},
    {key: 8, label: <MdFormatListBulleted size={'1.25em'}/>, style: 'unordered-list-item'},
    {key: 9, label: <MdFormatListNumbered size={'1.25em'}/>, style: 'ordered-list-item'},
    {key: 10, label: <MdCode size={'1.25em'}/>, style: 'code-block'},
    /*{key: 11, label: <MdInsertLink size={'1.25em'}/>, style: 'link'},*/
];

let INLINE_STYLES = [
    {key: 1, label: <MdFormatBold size={'1.25em'}/>, style: 'BOLD'},
    {key: 2, label: <MdFormatItalic size={'1.25em'}/>, style: 'ITALIC'},
    {key: 3, label: <MdFormatUnderlined size={'1.25em'}/>, style: 'UNDERLINE'},
    {key: 4, label: <span style={{fontFamily: 'monospace', lineHeight: '1rem', fontSize: '1rem'}}>Mono</span>, style: 'CODE'},
];

// Types Definition
//

//
// Controls Rendering Fn

const BlockStyleControls = (props) => {
    const {editorState} = props;
    const selection = editorState.getSelection();
    const blockType = editorState
        .getCurrentContent()
        .getBlockForKey(selection.getStartKey())
        .getType();

    return (
        <div className={props.className}>
            {BLOCK_TYPES.map((type) =>
                <Button
                    key={type.key}
                    active={type.style === blockType}
                    label={type.label}
                    onToggle={props.onToggle}
                    style={type.style}
                />
            )}
        </div>
    );
};

const InlineStyleControls = (props) => {
    let currentStyle = props.editorState.getCurrentInlineStyle();
    return (
        <div className={props.className}>
            {INLINE_STYLES.map(type =>
                <Button
                    key={type.key}
                    active={currentStyle.has(type.style)}
                    label={type.label}
                    onToggle={props.onToggle}
                    style={type.style}
                />
            )}
        </div>
    );
};

// Controls Rendering Fn
//

//
// Additional Components

class Line extends React.Component {
    render() {
        const showLineNo = false;
        const blockMap = this.props.contentState.getBlockMap().toArray();
        const blockKey = this.props.block.key;
        const lineNumber = blockMap.findIndex(block => blockKey === block.key) + 1;
        return (
            showLineNo ?
                <div style={{display: 'flex'}}>
                    <span style={{marginRight: '5px'}}>{lineNumber}</span>
                    <div style={{flex: '1'}}><EditorBlock {...this.props} /></div>
                </div>
                :
                <EditorBlock {...this.props} />
        );
    }
}

const blockRendererFn = function(contentBlock) {
    const type = contentBlock.getType();
    switch(type) {
        default:
            return {component: Line, editable: true}
    }
};

// Additional Components
//

//
// Main Editor

const initialState = {
    data: {},
    showMarkdownTooltip: false,
    loaded: false
};

class Editor extends React.Component {

    constructor(props) {
        super(props);

        let editorState = this.updateContent();
        this.state = {
            editorState,
            ...initialState
        };

        if(turnOnEmoji) {
            /*this.emojiPlugin = createEmojiPlugin({
                priorityList: {
                    ':see_no_evil:': ["1f648"],
                    ':raised_hands:': ["1f64c"],
                    ':100:': ["1f4af"],
                }
            });*/
        }

        this.plugins = [];

        this.plugins.push(createMarkdownShortcutsPlugin());
        if(turnOnEmoji) {
            //this.plugins.push(this.emojiPlugin);
        }

        this.onChange = (editorState) => this.setState({editorState});
        this._handleKeyCommand = this._handleKeyCommand.bind(this);
        this.onTab = (e) => this._onTab(e);
        this.toggleBlockType = (type) => this._toggleBlockType(type);
        this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);

        this.bindSave();
    }

    updateContent() {
        this.contentType = 'raw';

        let editorState = EditorState.createEmpty();
        if(this.props.content) {
            // raw draftjs
        } else if(this.props.markdown || '' === this.props.markdown) {
            // markdown
            this.contentType = 'markdown';
            if('' !== this.props.markdown) {
                editorState = EditorState.createWithContent(
                    convertFromRaw(markdownToDraft(this.props.markdown))
                );
            }
        } else if(this.props.html || '' === this.props.html) {
            // html
            this.contentType = 'html';

            if('' !== this.props.html && 'string' === typeof this.props.html) {
                // only when really string (and e.g. not bool) add prop as content
                // noinspection JSCheckFunctionSignatures
                editorState = EditorState.createWithContent(
                    stateFromHTML(this.props.html)
                );
            }
        }

        return editorState;
    }

    bindSave() {
        if(this.props.save) {
            switch(this.contentType) {
                case 'markdown':
                    this.props.save(this.getState.markdown);
                    break;

                case 'html':
                    this.props.save(this.getState.html);
                    break;

                default:
                case 'raw':
                    this.props.save(this.getState.raw);
                    break;
            }
        }

        if(this.props.saveRaw) {
            this.props.saveRaw(this.getState.raw);
        }
        if(this.props.saveMd) {
            this.props.saveMd(this.getState.markdown);
        }
        if(this.props.saveHtml) {
            this.props.saveHtml(this.getState.html);
        }
    }

    getState = {
        raw: () => {
            const content = this.state.editorState.getCurrentContent();
            if(content.hasText()) {
                return convertToRaw(content);
            }
        },
        markdown: () => {
            const content = this.state.editorState.getCurrentContent();
            if(content.hasText()) {
                return draftToMarkdown(convertToRaw(content));
            }
        },
        html: () => {
            const content = this.state.editorState.getCurrentContent();
            if(content.hasText()) {
                return stateToHTML(content);
            }
        }
    };

    _handleKeyCommand(command, editorState) {
        const newState = RichUtils.handleKeyCommand(editorState, command);
        if(newState) {
            this.onChange(newState);
            return 'handled';
        }
        return 'not-handled';
    }

    _onTab(e) {
        const maxDepth = 4;
        this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
    }

    _toggleBlockType(blockType) {
        this.onChange(
            RichUtils.toggleBlockType(
                this.state.editorState,
                blockType
            )
        );
    }

    _toggleInlineStyle(inlineStyle) {
        this.onChange(
            RichUtils.toggleInlineStyle(
                this.state.editorState,
                inlineStyle
            )
        );
    }

    componentWillUnmount() {
        this.setState(initialState);
    }

    componentDidMount() {
        let editorState = this.updateContent();
        this.setState({
            editorState,
            ...initialState
        });

        this.onChange = (editorState) => this.setState({editorState});

        this.bindSave();

        if(this.props.afterInit) {
            this.props.afterInit();
        }
    }

    render() {
        const {classes, t, theme} = this.props;
        const {editorState} = this.state;
        let {EmojiSuggestions, EmojiSelect} = {};
        if(turnOnEmoji) {
            EmojiSuggestions = this.emojiPlugin.EmojiSuggestions;
            EmojiSelect = this.emojiPlugin.EmojiSelect;
        }

        let className = classes.editor;
        let contentState = editorState.getCurrentContent();
        if(!contentState.hasText()) {
            if(contentState.getBlockMap().first().getType() !== 'unstyled') {
                className += classes.editorNoPlaceholder;
            }
        }

        let showBlockControl = !(this.props.onlyInline);

        return (
            <React.Fragment>
                {
                    ((this.props.content || this.props.markdown || this.props.html || '' === this.props.content || '' === this.props.markdown || '' === this.props.html) ? (
                        <div className={classes.wrapper}>
                            <div className={classes.wrapperControls}>
                                {showBlockControl ?
                                    <React.Fragment>
                                        <BlockStyleControls
                                            className={classes.controls}
                                            editorState={editorState}
                                            onToggle={this.toggleBlockType}
                                        />
                                        <div className={classes.separator} style={{background: theme.textColorLight}}/>
                                    </React.Fragment>
                                    : null}
                                <InlineStyleControls
                                    className={classes.controls}
                                    editorState={editorState}
                                    onToggle={this.toggleInlineStyle}
                                />
                                {turnOnEmoji ? <EmojiSelect/> : null}
                            </div>
                            <div className={className} onClick={this.focus}>
                                <div className={classes.markdown}>
                                    <div style={{position: 'relative'}}>
                                        <MarkdownLogo
                                            onMouseDown={() => {
                                                this.setState({showMarkdownTooltip: !this.state.showMarkdownTooltip})
                                            }}
                                            style={{opacity: 0.7, fill: theme.textColor, width: '1.25em', height: '1.25em', cursor: 'pointer'}}
                                        />
                                        <p className={classes.markdownTooltip + (this.state.showMarkdownTooltip ? ' open' : '')}
                                           style={{background: theme.bgPage, border: '1px solid ' + theme.textColorLight, transition: '0.4s linear background'}}><Trans i18nKey={'editor.markdownTooltip'}>Enabled <a
                                            href='https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet' target='_blank' rel='noopener noreferrer'>MarkDown</a> Syntax</Trans></p>
                                    </div>
                                </div>
                                <DraftEditor
                                    blockStyleFn={getBlockStyle}
                                    blockRendererFn={blockRendererFn}
                                    customStyleMap={styleMap}
                                    editorState={editorState}
                                    handleKeyCommand={this.handleKeyCommand}
                                    onChange={this.onChange}
                                    onTab={this.onTab}
                                    placeholder={t('editor.placeholder')}
                                    ref="editor"
                                    spellCheck={true}
                                    plugins={this.plugins}
                                />
                                {/* todo: emoji suggestions doesn't popup on typing e.g. `:see_` */}
                                {turnOnEmoji ? <EmojiSuggestions/> : null}
                            </div>
                        </div>
                    ) : null)
                }
            </React.Fragment>
        )
    };
}

//
/**
 * DO NOT include Editor on its own, use wrapped EditorLoader - to have an animation during loading of components
 */

export default withNamespaces('editor')(withTheme(injectSheet(styling)(Editor)));