import React, {HTMLProps, useEffect, useRef, useState} from "react";
import {Utils} from "./utils";
import {DEFAULT_TOOLBARS} from "./constants";


export default React.forwardRef<UEditorInstance | undefined, UEditorProps>((props, ref) => {
    const {container, options, value, disabled, plugins = [], hidden, onChange, ...rest} = props;
    const [id] = useState(Utils.createHtmlId);
    const [editor, setEditor] = useState<UEditorInstance>();
    const instance = useRef<UEditorInstance>();
    React.useImperativeHandle(ref, () => {
        return editor;
    }, [editor]);
    useEffect(() => {
        instance.current = editor;
        if (editor) {
            const callback = () => {
                if (typeof onChange === "function") {
                    onChange(editor.getContent());
                }
            }
            editor.addListener("contentChange", callback);
            return () => {
                if (editor) {
                    editor.removeListener("contentChange", callback);
                }
            }
        }
    }, [editor, onChange]);
    useEffect(() => {
        if (value && editor) {
            if (editor.getContent() !== value) {
                editor.setContent(value);
            }
        }
    }, [editor, value]);
    useEffect(() => {
        if (editor) {
            if (disabled !== undefined) {
                if (disabled) {
                    editor.setDisabled();
                } else {
                    editor.setEnabled();
                }
            }
        }
    }, [editor, disabled]);

    useEffect(() => {
        if (editor) {
            plugins.map(item => {
                editor.commands[item.name] = {
                    execCommand: () => {
                        return item.onClick(editor);
                    },
                    queryCommandState: () => {},
                }
            })
        }
    }, [plugins, editor]);

    return (
        <div {...container}>
            {plugins.map(item => (
                <style key={item.name}>
                    {`.edui-for-${item.name} .edui-icon {
                        background-position: ${item.iconPosition};
                    }`}
                </style>
            ))}
            <div
                {...rest}
                id={id}
                ref={() => {
                    if (!editor) {
                        const toolbars = options && options.toolbars ? options.toolbars : [DEFAULT_TOOLBARS];
                        const labelMap = options && options.labelMap ? options.labelMap : {};
                        plugins.map(item => {
                            if (item.label) {
                                labelMap[item.name] = item.label;
                            }
                            return item.name;
                        })
                        const ued = UE.getEditor(id, {
                            UEDITOR_HOME_URL: "",
                            ...options,
                            labelMap,
                            toolbars: toolbars,
                        });
                        setEditor(ued);
                    }
                }}
            />
        </div>
    )
})

declare namespace UE {
    function getEditor(id: string, options?: UEditorInstanceOptions): UEditorInstance;
}

export interface UEditorProps extends Omit<HTMLProps<HTMLDivElement>, "onChange"> {
    options?: UEditorInstanceOptions;
    value?: string;
    onChange?: (value: string) => void;
    disabled?: boolean;
    plugins?: UEditorPlugin[];
    container?: HTMLProps<HTMLDivElement>;
}

export interface UEditorPlugin {
    name: string;
    iconPosition: string;
    label?: string;
    onClick: (editor: UEditorInstance) => boolean;
}

export interface UEditorInstanceOptions {
    toolbars?: string[][];
    /**
     * 为编辑器实例添加一个路径，这个不能被注释
     */
    UEDITOR_HOME_URL: string;
    /**
     * [默认值：URL + "php/controller.php"]
     * 服务器统一请求接口路径
     */
    serverUrl?: string;
    /**
     * 参数格式是键值对，键名对应toolbar参数的项：{"bold": "加粗"} ]，当鼠标放在工具栏上时显示的tooltip提示，
     * 留空支持自动多语言配置，否则以配置值为准
     */
    labelMap?: Record<string, string>;
    /**
     * 语言 默认 zh-cn
     */
    lang?: string;
    /**
     * 语言包目录名称 lang/zh-cn/
     */
    langPath?: string;
    /**
     * 主题 默认 default
     */
    theme?: string;
    /**
     * 主题包目录名称  theme/default/
     */
    themePath?: string;
    /**
     * 编辑器在页面上的z-index层级的基数，默认是900
     */
    zIndex?: number;
    /**
     * 针对getAllHtml方法，会在对应的head标签中增加该编码设置。
     * 默认 utf-8
     */
    charset?: string;
    /**
     * 若实例化编辑器的页面手动修改的domain，此处需要设置为true
     * 默认 false
     */
    customDomain?: boolean;
    /**
     * 默认显示编辑器
     */
    isShow?: boolean;
    /**
     * 提交表单时，
     * 服务器获取编辑器提交内容的所用的参数，
     * 多实例时可以给容器name属性，
     * 会将name给定的值最为每个实例的键值，
     * 不用每次实例化的时候都设置这个值
     * 默认 editorValue
     */
    textarea?: string;
    /**
     * 初始化编辑器的内容
     */
    initialContent?: string;
    /**
     * 是否自动清除编辑器初始内容，
     * 注意：如果focus属性设置为true，这个也为真，
     * 那么编辑器一上来就会触发导致初始化的内容看不到了
     * 默认 true
     */
    autoClearinitialContent?: boolean;
    /**
     * 初始化时，是否让编辑器获得焦点true或false
     * 默认 false
     */
    focus?: boolean;
    /**
     * 编辑器层级的基数，可以用来改变字体等 //如果自定义，最好给p标签如下的行高，要不输入中文时，会有跳动感
     * 默认值 p{line-height:1em}
     */
    initialStyle?: string;
    /**
     * 给编辑器内部引入一个css文件
     * 默认 '/themes/iframe.css'
     */
    iframeCssUrl?: string;
    /**
     * 首行缩进距离，默认是2em
     */
    indentValue?: string;
    /**
     * 初始化编辑器宽度，默认1000
     */
    initialFrameWidth?: number;
    /**
     * 初始化编辑器高度，默认320
     */
    initialFrameHeight?: number;
    /**
     * 编辑器初始化结束后，编辑区域是否是只读的，默认是false
     */
    readonly?: boolean;
    /**
     * getContent时，是否删除空的inlineElement节点（包括嵌套的情况）
     */
    autoClearEmptyNode?: boolean;
    /**
     * 启用自动保存
     * 默认值：true
     */
    enableAutoSave?: boolean;
    /**
     * 自动保存间隔时间，单位ms
     * 默认值：500
     */
    saveInterval?: number;
    /**
     * 启用图片拉伸缩放
     * 默认值：true
     */
    imageScaleEnabled?: boolean;
    /**
     * 是否开启初始化时即全屏，默认关闭 false
     */
    fullscreen?: boolean;
    /**
     * 图片操作的浮层开关，默认打开 true
     */
    imagePopup?: boolean;
    /**
     * 自动同步编辑器要提交的数据
     * 默认值：true
     */
    autoSyncData?: boolean;
    /**
     * 是否开启表情本地化，默认关闭。若要开启请确保emotion文件夹下包含官网提供的images表情文件夹
     * 默认值：false
     */
    emotionLocalization?: boolean;
    /**
     * 粘贴只保留标签，去除标签所有属性
     * 默认值：false
     */
    retainOnlyLabelPasted?: boolean;
    /**
     * 是否默认为纯文本粘贴。false为不使用纯文本粘贴，true为使用纯文本粘贴
     * 默认值：false
     */
    pasteplain?: boolean;
    /**
     * 纯文本粘贴模式下的过滤规则
     */
    filterTxtRules?: object;
    /**
     * 提交到后台的数据是否包含整个html字符串
     * 默认值：false
     */
    allHtmlEnabled?: boolean;
    /**
     * 有序列表的下拉配置，值留空时支持多语言自动识别，若配置值，则以此值为准
     * ```js
     * {
     *   //自定的样式
     *   'num': '1,2,3...',
     *   'num1': '1),2),3)...',
     *   'num2': '(1),(2),(3)...',
     *   'cn': '一,二,三....',
     *   'cn1': '一),二),三)....',
     *   'cn2': '(一),(二),(三)....',
     *   //系统自带
     *   'decimal': '' , '1,2,3...'
     *   'lower-alpha': '' , // 'a,b,c...'
     *   'lower-roman': '' , //'i,ii,iii...'
     *   'upper-alpha': '' , //'A,B,C'
     *   'upper-roman': '' //'I,II,III...'
     * }
     * ```
     */
    insertorderedlist?: object;
    /**
     * 无序列表的下拉配置，值留空时支持多语言自动识别，若配置值，则以此值为准
     * ```js
     * { //自定的样式
     *   'dash': '— 破折号', //-破折号
     *   'dot': ' 。 小圆圈', //系统自带
     *   'circle': '', // '○ 小圆圈'
     *   'disc': '', // '● 小圆点'
     *   'square': '' //'■ 小方块'
     * }
     * ```
     */
    insertunorderedlist?: object;
    /**
     *  [默认值：'30']
     *  默认的左边缩进的基数倍
     */
    listDefaultPaddingLeft?: number;
    /**
     * [默认值：'http://bs.baidu.com/listicon/']
     * 自定义标号的路径
     */
    listiconpath?: string;
    /**
     * [默认值：3]
     * 限制可以tab的级数， 设置-1为不限制
     */
    maxListLevel?: number;
    /**
     *  [默认值：false]
     *  禁止word中粘贴进来的列表自动变成列表标签
     */
    autoTransWordToList?: boolean;
    /**
     * 字体设置 label留空支持多语言自动切换，若配置，则以配置值为准
     * ```js
     * [
     *      {
     *           label: '',
     *           name: 'songti',
     *           val: '宋体,SimSun'
     *      }
     * ]
     * ```
     */
    fontfamily?: Array<{
        name: string;
        label: string;
        val: string;
    }>;
    /**
     * 字号
     */
    fontsize?: number[];
    /**
     * 段落格式 值留空时支持多语言自动识别，若配置，则以配置值为准
     * ```js
     * //默认值：
     * {
     *    'p': '',
     *    'h1': '',
     *    'h2': '',
     *    'h3': '',
     *    'h4': '',
     *    'h5': '',
     *    'h6': ''
     * }
     * ```
     */
    paragraph?: Record<string, string>;
    /**
     * 段间距 值和显示的名字相同
     * 默认值：['5', '10', '15', '20', '25']
     */
    rowspacingtop?: number[];
    /**
     * 段间距 值和显示的名字相同
     * 默认值：['5', '10', '15', '20', '25']
     */
    rowspacingbottom?: number[];
    /**
     * 行内间距 值和显示的名字相同
     * 默认值：['1', '1.5','1.75','2', '3', '4', '5']
     */
    lineheight?: number[];
    customstyle?: Array<{
        /**
         * tag 使用的标签名字
         */
        tag: string;
        name: string;
        /**
         * label 显示的名字也是用来标识不同类型的标识符，注意这个值每个要不同
         */
        label: string;
        /**
         * style 添加的样式
         */
        style: string;
    }>;

    /**
     * 默认值：true
     * 打开右键菜单功能
     */
    enableContextMenu?: boolean;
    contextMenu?: Array<{
        /**
         * 显示的名称
         */
        label: string;
        /**
         * 执行的command命令，当点击这个右键菜单时
         */
        cmdName?: string;
        /**
         * exec可选，有了exec就会在点击时执行这个function，优先级高于cmdName
         */
        exec?(): void;
    }>;

    /**
     * 快捷菜单
     * 默认值 ["fontfamily", "fontsize", "bold", "italic", "underline", "forecolor", "backcolor", "insertorderedlist", "insertunorderedlist"]
     */
    shortcutMenu?: string[];
    /**
     * 是否启用元素路径，默认是显示 默认值：true
     */
    elementPathEnabled?: boolean;
    /**
     * 是否开启字数统计 默认值：true;
     */
    wordCount?: boolean;
    /**
     * 允许的最大字符数
     * 默认值：1000
     */
    maximumWords?: number;
    /**
     * 当前已输入 {#count} 个字符，您还可以输入{#leave} 个字符，字数统计提示，{#count}代表当前字数，{#leave}代表还可以输入多少字符数，
     * 留空支持多语言自动切换，否则按此配置显示
     * 默认值：'当前已输入{#count}个字符, 您还可以输入{#leave}个字符。 '
     */
    wordCountMsg?: string;
    /**
     * 超出字数限制提示 留空支持多语言自动切换，否则按此配置显示
     * 默认值：'<span style="color:red;">你输入的字符个数已经超出最大允许值，服务器可能会拒绝保存！</span>'
     */
    wordOverFlowMsg?: string;
    /**
     * 点击tab键时移动的距离，tabSize倍数，tabNode什么字符做为单位
     * 默认值：4
     */
    tabSize?: number;
    /**
     * 默认值：'&nbsp;'
     */
    tabNode?: string;
    /**
     * 清除格式时可以删除的标签和属性
     * 默认值：'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var'
     */
    removeFormatTags?: string;
    /**
     * undo操作，可以最多回退的次数，默认20
     */
    maxUndoCount?: number;
    /**
     * undo操作，当输入的字符数超过该值时，保存一次现场
     */
    maxInputCount?: number;
    /**
     * 是否自动长高，默认true
     */
    autoHeightEnabled?: boolean;
    /**
     * 是否可以拉伸长高，默认true(当开启时，自动长高失效)
     * 默认值：false
     */
    scaleEnabled?: boolean;
    /**
     * 编辑器拖动时最小宽度，默认800
     */
    minFrameWidth?: number;
    /**
     * 编辑器拖动时最小高度，默认220
     */
    minFrameHeight?: number;
    /**
     * 是否保持toolbar的位置不动，默认true
     */
    autoFloatEnabled?: boolean;
    /**
     * 浮动时工具栏距离浏览器顶部的高度，用于某些具有固定头部的页面
     * 默认值：30
     */
    topOffset?: number;
    /**
     * 编辑器底部距离工具栏高度(如果参数大于等于编辑器高度，则设置无效)
     * 默认值：400
     */
    toolbarTopOffset?: number;
    /**
     * 分页标识符，默认是 "ueditorpagebreaktag"
     */
    pageBreakTag?: string;
    /**
     * 自动排版参数
     */
    autotypeset?: AutoTypeSet;
    /**
     * 表格是否可以拖拽
     * 默认值：true
     */
    tableDragable?: boolean;
    /**
     * 禁止表格嵌套
     * 默认值：true
     */
    disabledTableInTable?: boolean;
    /**
     * 源码的查看方式，codemirror是代码高亮，textarea是文本框，默认是codemirror，注意默认codemirror只能在ie8+和非ie中使用
     * 默认值："codemirror"
     */
    sourceEditor?: "codemirror";
    /**
     * [默认值：URL + "third-party/codemirror/codemirror.js"]
     * 如果sourceEditor是codemirror需要配置这项，codeMirror js加载的路径
     */
    codeMirrorJsUrl?: string;
    /**
     *[默认值：URL + "third-party/codemirror/codemirror.css"]
     * 如果sourceEditor是codemirror需要配置这项，codeMirror css加载的路径
     */
    codeMirrorCssUrl?: string;
    /**
     * [默认值：false]
     * 编辑器初始化完成后是否进入源码模式，默认为否。
     */
    sourceEditorFirst?: boolean;
    /**
     * dialog内容的路径 ～会被替换成URL，垓属性一旦打开，将覆盖所有的dialog的默认路径
     */
    iframeUrlMap?: Record<string, string>;
    /**
     * 默认会将外部进入的html数据中的Div标签转换成P标签，外部进入的数据包括粘贴和调用setContent接口进入编辑器的数据。
     * 如果设置成false将不在做这个转换。
     */
    allowDivTransToP?: boolean;
}

export interface AutoTypeSet {
    /**
     * 合并空行
     * 默认值：true
     */
    mergeEmptyline?: boolean;
    /**
     * 去掉冗余的class
     * 默认值：true
     */
    removeClass?: boolean;
    /**
     * 去掉空行
     * 默认值：false
     */
    removeEmptyline?: boolean;
    /**
     * 段落的排版方式，可以是 left，right，center，justify 去掉这个属性表示不执行排版
     * 默认值: left
     */
    textAlign?: 'left' | 'right' | 'center' | 'justify';
    /**
     * 图片的浮动方式，独占一行剧中，左右浮动，默认: center，left，right，none 去掉这个属性表示不执行排版
     */
    imageBlockLine?: 'center' | 'left' | 'right' | 'none';
    /**
     * 根据规则过滤没事粘贴进来的内容
     * 默认值：false
     */
    pasteFilter?: boolean;
    /**
     * 去掉所有的内嵌字号，使用编辑器默认的字号
     * 默认值：false
     */
    clearFontSize?: boolean;
    /**
     * 去掉所有的内嵌字体，使用编辑器默认的字体
     * 默认值：false
     */
    clearFontFamily?: boolean;
    /**
     * 去掉空节点
     * 默认值：false
     */
    removeEmptyNode?: boolean;
    /**
     * 可以去掉的标签
     */
    removeTagNames?: Record<string, 1>;
    /**
     * 行首缩进
     * 默认值：false
     */
    indent?: boolean;
    /**
     * 行首缩进的大小
     * 默认值：2em
     */
    indentValue?: string;
    /**
     * 默认值：false
     */
    bdc2sb?: boolean;
    /**
     * 默认值：false
     */
    tobdc?: boolean;
}

export interface UEditorInstance {
    commands: Record<string, {
        execCommand: () => boolean;
        queryCommandState: () => void;
    }>;
    /**
     * 是否就绪
     * @param callback 就绪后的回调
     */
    ready: (callback: () => void) => void;
    /**
     * 设置新的内容
     * @param content html标签内容
     * @param replace 是否替换
     */
    setContent: (content: string, replace?: boolean) => void;
    /**
     * 获取html内容
     */
    getContent: () => string;
    /**
     * 获取存文本内容
     */
    getContentTxt: () => string;
    /**
     * 获取保留格式的文本内容
     */
    getPlainTxt: () => string;
    /**
     * 判断编辑器是否有内容
     */
    hasContents: () => boolean;
    /**
     * 获取焦点
     */
    focus: () => void;
    /**
     * 断编辑器是否获得焦点
     */
    isFocus: () => boolean;
    /**
     * 设置当前编辑区域不可编辑
     */
    setDisabled: () => void;
    /**
     * 设置当前编辑区域可以编辑
     */
    setEnabled: () => void;
    /**
     * 隐藏编辑器
     */
    setHide: () => void;
    /**
     * 显示编辑器
     */
    setShow: () => void;
    /**
     * 选区对象
     */
    selection: UEditorSelection;
    /**
     * 在当前光标位置插入html内容
     * @param cmd 指令
     * @param content html内容
     */
    execCommand: <K extends keyof UEditorCommand>(cmd: K, content: UEditorCommand[K]) => void;

    removeListener(event: string, callback: (...args: any[]) => void): void;

    /**
     * 实例的事件监听
     * @param event 事件名称
     * @param callback 事件回调
     */
    addListener(event: string, callback: (...args: any[]) => void): void;
}

export interface UEditorCommand {
    /**
     * 在当前光标位置插入html内容
     */
    inserthtml: string;
    /**
     * 加粗
     */
    bold: undefined;
    /**
     * 加斜线
     */
    italic: undefined;
    /**
     * 设置上标
     */
    subscript: undefined;
    /**
     * 设置下标
     */
    supscript: undefined;
    /**
     * 设置字体颜色
     */
    forecolor: string;
    /**
     * 设置字体背景颜色
     */
    backcolor: string;
    /**
     * 回退编辑器
     */
    undo: undefined;
    /**
     * 撤销回退
     */
    redo: undefined;
    /**
     * 切换源码和可视化编辑
     */
    source: undefined;
    /**
     * 选中所有内容
     */
    selectall: undefined;
    /**
     * 清空内容
     */
    cleardoc: undefined;
    /**
     * 清空内容
     */
    drafts: undefined;
    /**
     * 清空草稿箱
     */
    clearlocaldata: undefined;
}

export interface UEditorSelection {
    /**
     * 获得当前选中的文本
     */
    getText: () => string;
}
