<template>
    <div :class="[$style.quill, {'quill--mini': mini}, {[$style.mini]: mini}]" @click.stop="() => {}">
        <div
            ref="textarea"
            class="notranslate quill-text"
            :class="$style.textarea"
            @click="onTextAreaClick">
        </div>
    </div>
</template>

<script>
import shortid from 'shortid';
import debounce from 'lodash.debounce';
import generatePaletteClasses from './generatePaletteClasses.js';
// import tinycolor from 'tinycolor2';
import { Helper as QuillStore } from './store/index.js';
import Quill from './quill-read-only';
import uniqBy from 'lodash.uniqby';

export default {
    props: {
        id: String,
        contents: Object,
        mini: Boolean,
    },

    data() {
        return {
            uid: shortid.generate(),
            quill: null,
            ready: false,
            highlights: [],
            highlightsIds: [],
            isSelected: false,
            formatsState: {
                highlights: {
                    formats: [],
                    ids: [],
                },
                notes: {
                    formats: [],
                    ids: [],
                },
            },
        };
    },

    computed: {
        ...QuillStore.mapState({
            palette: (state) => state.palette,
        }),
    },

    watch: {
        contents(v) {
            this.quill.setContents(v);
        },
    },

    created() {
        if (process.browser) {
            generatePaletteClasses(this.palette);
        }
    },

    mounted() {
        this.quill = new Quill(this.$refs.textarea, {
            readOnly: true,
        });

        if (this.contents) {
            this.quill.setContents(this.contents);
        }

        const onTextChange = debounce(this.onTextChange, 500);
        this.quill.on('text-change', onTextChange);
        this.quill.on('selection-change', this.onTextSelect);

        // document.body.addEventListener('mouseup', () => {
        //     const selection = window.getSelection();
        //     const range = selection.getRangeAt(0);

        //     if (range.startContainer !== range.endContainer) {
        //         if (this.$refs.textarea.contains(range.startContainer)) {
        //             this.onTextSelect({
        //                 index: range.startOffset,
        //                 length: this.quill.getLength() - range.startOffset,
        //             });
        //         } else if (this.$refs.textarea.contains(range.endContainer)) {
        //             this.onTextSelect({
        //                 index: 0,
        //                 length: range.endOffset,
        //             });
        //         }
        //     }
        // });

        document.body.addEventListener('click', this.close);
        document.body.addEventListener('quill-focus', (e) => {
            if (e.detail.id !== this.uid) {
                this.close();
            } else {
                // clearTimeout(this.deselectTimer);
            }
        });

        document.body.addEventListener('quill-text-select', () => {
            if (this.deselectTimer) {
                clearTimeout(this.deselectTimer);
            }
        });

        if (window.AndroidProxy && window.AndroidProxy.content) {
            const map = [
                ['highlight-text', this.onAndroidHighlightText],
                ['insert-note', this.onAndroidInsertNote],
                ['deselect-text', this.onAndroidDeselectText],
            ];

            for (const [name, cb] of map) {
                window.AndroidProxy.content.addEventListener(name, cb);
            }

            this.$on('hook:beforeDestroy', () => {
                for (const [name, cb] of map) {
                    window.AndroidProxy.content.removeEventListener(name, cb);
                }
            });
        }

        // document.addEventListener('android-highlight-text', this.onAndroidHighlightText);
        // document.addEventListener('android-deselect-text', this.onAndroidDeselectText);

        this.$once('hook:beforeDestroy', () => {
            document.body.removeEventListener('click', this.close);
            // document.removeEventListener('android-highlight-text', this.onAndroidHighlightText);
            // document.removeEventListener('android-deselect-text', this.onAndroidDeselectText);
        });

        this.ready = true;
    },

    methods: {
        open() {
            // console.log('open');
        },

        close() {
            // console.log('close');
        },

        onTextAreaClick() {
            this.$el.dispatchEvent(
                new CustomEvent('quill-focus', {
                    bubbles: true,
                    detail: {
                        id: this.uid,
                    },
                }),
            );
        },

        onTextChange() {
            this.quill.update();
        },

        overlaps(type, start, end) {
            return this.formatsState[type].formats.some((h) => (
                start > h.start && start < h.end)
                || (end > h.start && end < h.end)
                || (h.start > start && h.end < end)
                || (h.start === start && h.end === end),
            );
        },

        onTextSelect(range) {
            if (!range || !range.length) {
                if (this.isSelected) {
                    if (window.AndroidContent) {
                        this.deselectTimer = setTimeout(() => {
                            window.AndroidContent.onDeselectText();
                        }, 50);
                    }
                    this.isSelected = false;
                }

                return;
            }

            this.$el.dispatchEvent(
                new CustomEvent('quill-text-select', {
                    bubbles: true,
                    detail: {
                        id: this.uid,
                    },
                }),
            );

            const start = range.index;
            const end = range.index + range.length;

            if (this.overlaps('highlights', start, end) || this.overlaps('notes', start, end)) {
                if (window.AndroidContent) {
                    window.AndroidContent.hideToolbar();
                }

                return;
            }

            if (window.AndroidContent) {
                const contents = this.quill.getContents(range.index, range.length);

                const text = contents.ops.reduce((acc, op) => {
                    if (typeof op.insert === 'string') {
                        acc += op.insert;
                    } else if (op.insert.verse) {
                        acc += op.insert.verse.ref;
                    }

                    return acc;
                }, '');

                if (text.trim().length > 0) {
                    window.AndroidContent.onTextSelected(
                        `${this.id}:${range.index}:${range.length}`,
                        range.index,
                        range.length,
                        text,
                    );
                }
            }

            // this.quill.formatText(range.index, range.length, 'color', 'red');
            // this.quill.formatText(range.index, range.length, 'bold', true);

            this.isSelected = true;
        },

        transformFormatsArray(formats) {
            return uniqBy(formats, (h) => h.id).reduce((acc, format) => {
                if (!format.id) {
                    return acc;
                }

                const [componentId] = format.id.split(':');

                if (componentId === this.id) {
                    const start = format.index;
                    const end = format.index + format.length;
                    acc.push({
                        start,
                        end,
                        ...format,
                    });
                }

                return acc;
            }, []);
        },

        removeAllFormats(type) {
            const { formats } = this.formatsState[type];

            if (formats.length > 0) {
                this.formatsState[type].ids = [];

                for (const format of formats) {
                    this.quill.removeFormat(format.index, format.length);
                }

                this.formatsState[type].formats = [];
            }
        },

        applyFormats(type, name, formats) {
            const state = this.formatsState[type];

            if (state.formats.length === 0) {
                for (const format of formats) {
                    if (this.overlaps(type, format.start, format.end)) {
                        continue;
                    }

                    this.quill.formatText(format.index, format.length, name, format);

                    state.formats.push(format);
                }
            } else {
                for (const format of formats) {
                    const isNew = !state.ids.includes(format.id);
                    const update = {
                        apply: false,
                    };

                    if (!isNew) {
                        const idx = state.formats.findIndex((h) => h.id === format.id);
                        if (idx >= 0) {
                            update.apply = false;
                            const ignore = ['id', 'index', 'length', 'start', 'end'];
                            for (const [key, value] of Object.entries(state.formats[idx])) {
                                if (ignore.includes(key)) {
                                    continue;
                                }

                                if (format[key] !== value) {
                                    update.apply = true;
                                    break;
                                }
                            }

                            update.index = idx;
                        }
                    }

                    if (!isNew && !update.apply) {
                        continue;
                    }

                    if (isNew && this.overlaps(type, format.start, format.end)) {
                        continue;
                    }

                    if (isNew || update.apply) {
                        this.quill.formatText(format.index, format.length, name, format);
                    }

                    if (update.apply) {
                        state.formats.splice(update.index, 1, format);
                    } else {
                        state.formats.push(format);
                    }
                }

                const ids = formats.map((h) => h.id);
                const deletedIds = state.ids.filter((id) => !ids.includes(id));

                for (const id of deletedIds) {
                    const idx = state.formats.findIndex((h) => h.id === id);

                    this.quill.removeFormat(state.formats[idx].index, state.formats[idx].length);
                    if (idx >= 0) {
                        state.formats.splice(idx, 1);
                    }
                }
            }

            state.ids = state.formats.map((h) => h.id);
        },

        onAndroidHighlightText(e) {
            const formats = e.detail;
            if (!this.isValid(formats)) {
                return;
            }

            const type = 'highlights';
            const name = 'highlight';

            const highlights = this.transformFormatsArray(formats);

            if (highlights.length === 0) {
                this.removeAllFormats(type);

                return;
            }

            this.applyFormats(type, name, highlights);
        },

        onAndroidInsertNote(e) {
            const formats = e.detail;
            if (!this.isValid(formats)) {
                return;
            }

            const type = 'notes';
            const name = 'note';

            const notes = this.transformFormatsArray(formats);

            if (notes.length === 0) {
                this.removeAllFormats(type);

                return;
            }

            this.applyFormats(type, name, notes);
        },

        isValid(formats) {
            if (!this.id) {
                console.log('Component id is undefined', this.$el);

                return false;
            }

            if (!Array.isArray(formats)) {
                console.log('Invalid highlights', formats);

                return false;
            }

            return true;
        },

        hid(format) {
            return `${this.id}:${format.index}:${format.length}`;
        },

        onAndroidDeselectText() {
            this.quill.blur();
        },
    },
};
</script>

<style module src="./Quill.css"></style>
