<template>
    <div class="w-100 block-editor"
         :dir="direction"
         :style="{'text-align': direction === 'rtl' ? 'right' : 'left'}">
        <j-dragger :list="value.blocks"
                   :can-delete="canDelete"
                   :group="dragGroup"
                   @delete="onDelete"
                   :direction="direction">
            <keep-alive slot-scope="{item, index}">
                <component :is="`block-${item.type}`"
                           :value="item.data"
                           :config="getConfig(item.type)"
                           :ref="`blocks_${index}`"
                           @input="e => blockDataUpdated(item, index, e)"></component>
            </keep-alive>
        </j-dragger>
        <div class="w-100 d-flex flex-row mt-2">
            <j-dropdown v-model="addDropdownVisible"
                        style="min-width: 400px"
                        v-show="canAdd">
                <template slot="button">
                    <span class="mdi mdi-18px mdi-plus"></span>
                    &nbsp; {{$t('addNew')}}
                    &nbsp;
                </template>
                <div style="max-height: 200px;position:relative;overflow-y:scroll"
                     class="w-100">
                    <b-dropdown-item-button v-for="item in moduleTypes"
                                            :key="item"
                                            @click="onAdd(item)">
                        <p class="pb-1 mb-0"
                           style="font-size: 1rem">{{$t(`${item}.type`)}}</p>
                        <p class="pb-1 mb-0"
                           style="font-size: 0.8rem">{{$t(`${item}.description`)}}</p>
                        &nbsp;
                    </b-dropdown-item-button>
                </div>
            </j-dropdown>
            <button class="btn btn-danger mx-3"
                    @click="onDeleteAll"
                    :title="$t('deleteAll')"
                    v-show="canDeleteAll">
                <span class="mdi mdi-18px mdi-close"></span>
            </button>
        </div>
    </div>
</template>

<script>
import { i18n } from "../../localization/vue-i18n";
import { cultureManager, guid } from "libj-core";
import { blockEditorRegistry } from "./BlockEditorRegistry";
import { isVoid, removeMany } from "./util";
import isString from 'lodash.isstring';
import loc from './j-block-editor.resx';
export default {
    i18n: i18n(loc),
    props: {
        /**
         * object of type {
         *  blocks: [
         *      {
         *          type: '',   //block type
         *          data: {}    //block custom data
         *      }
         *  ]
         * }
         */
        value: {
            type: Object,
            default() {
                return {
                    blocks: []
                };
            }
        },
        /**
         * array of enabled modules (vue components) and their config [
         *      {
         *          type: '',       //module type
         *          config: {},     //module config
         *      }
         * ]
         */
        modules: {
            type: Array,
            default() {
                return [];
            }
        },
        culture: {
            default: cultureManager.getActiveCulture().code
        },
        canAdd: {
            default: true
        },
        canDelete: {
            default: true
        },
        canDeleteAll: {
            default: true
        }
    },
    data() {
        return {
            dragGroup: guid.newId(),
            //enabled modules
            enabledModules: [],
            dragging: false,
            //valid modules to add
            moduleTypes: [],
            addDropdownVisible: false
        };
    },
    created() {
        var regLoc = blockEditorRegistry.getLoc();
        Object.keys(regLoc).forEach(culture => {
            this.$i18n.mergeLocaleMessage(culture, regLoc[culture]);
        });
        this.onModulesUpdated();
    },
    methods: {
        isBlockValid(b) {
            if (isVoid(b)) {
                return false;
            }
            var d = this.getModuleDescriptor(b.type);
            if (isVoid(d)) {
                return false;
            }
            var config = this.getConfig(b.type);
            var res = d.isValid(b, config);
            if (window.debug_blockEditor && !res) {
                console.log(`isBlockValid: ${JSON.stringify(b)} -> false`);
            }
            return res;
        },
        isModuleValid(c) {
            if (isVoid(c)) {
                return false;
            }
            return !isVoid(c.type) && isString(c.type);
        },
        getEnabledModuleTypes() {
            var res = blockEditorRegistry.getTypes();
            return res.filter(a => {
                var i = this.enabledModules.findIndex(m => {
                    return `block-${m.type}` === a;
                });
                return i > -1;
            });
        },
        getConfig(type) {
            var d = this.getModuleDescriptor(type);
            if (isVoid(d)) {
                console.log(`module descriptor ${type} not found`);
                return;
            }
            var emptyConfig = d.getEmptyConfig();
            var mm = this.enabledModules.find(m => {
                return m.type === type;
            });
            if (isVoid(mm)) {
                console.log(`module ${type} not found`);
                return;
            }
            var c = mm.config;
            if (isVoid(c)) {
                return emptyConfig;
            }
            var res = Object.assign({}, emptyConfig, c);
            return res;
        },
        onModulesUpdated() {
            this.enabledModules.splice(0, this.enabledModules.length);
            this.modules.forEach(m => {
                //validate module
                if (this.isModuleValid(m)) {
                    this.enabledModules.push(m);
                } else {
                    console.log(m, 'is not a valid module');
                }
            });
            //update blocks
            this.onValueBlocksUpdated();
            //update module types
            this.moduleTypes.splice(0, this.moduleTypes.length);
            var mTypes = this.getEnabledModuleTypes();
            mTypes.forEach(type => this.moduleTypes.push(type));
        },
        onValueBlocksUpdated() {
            var removeIndices = [];
            this.value.blocks.forEach((b, index) => {
                if (!this.isBlockValid(b)) {
                    removeIndices.push(index);
                    return;
                }
                //check if there's an enabled module for this block
                var i = _.findIndex(this.enabledModules, m => {
                    return m.type === b.type;
                });
                if (i < 0) {
                    removeIndices.push(index);
                    return;
                }
            });
            removeMany(this.value.blocks, removeIndices);
        },
        onDelete(args) {
            const { item, index } = args;
            args.cancel = true;

            var block = this.value.blocks[index];
            var eventArgs = {
                block: block,
                cancel: false
            };
            this.$emit('deleting', eventArgs);
            if (eventArgs.cancel === true) {
                return;
            }

            this.$toast.ask({
                onYes: () => {
                    this.value.blocks.splice(index, 1);
                }
            });
        },
        onDeleteAll() {
            var eventArgs = { cancel: false };
            this.$emit('deleting-all', eventArgs);
            if (eventArgs.cancel === true) {
                return;
            }
            this.$toast.ask({
                onYes: () => {
                    this.value.blocks.splice(0, this.value.blocks.length);
                }
            });
        },
        onAdd(type) {
            var eventArgs = {
                type: type,
                cancel: false
            };
            this.$emit('adding', eventArgs);
            if (eventArgs.cancel === true) {
                return;
            }
            var d = this.getModuleDescriptor(type);
            if (isVoid(d)) {
                console.log(`no descriptor for ${type}`);
                return;
            }
            this.addDropdownVisible = false;
            var value = d.getEmpty();
            this.value.blocks.push(value);
        },
        blockDataUpdated(block, index, data) {
            var b = this.value.blocks[index];
            if (window.debug_blockEditor) {
                console.log(`- ${JSON.stringify(b.data)}`);
            }
            var d = this.getModuleDescriptor(b.type);
            d.mergeData(b.data, data);
            if (window.debug_blockEditor) {
                console.log(`+ ${JSON.stringify(b.data)}`);
            }
        },
        getModuleDescriptor(type) {
            type = type.startsWith('block-') ? type : `block-${type}`;
            var m = blockEditorRegistry.getModules()
            if (isVoid(m) || isVoid(m[type])) {
                return null;
            }
            return m[type].descriptor;
        }
    },
    watch: {
        'value.blocks': {
            handler() {
                this.onValueBlocksUpdated();
            },
            deep: true
        },
        modules: {
            handler() {
                this.onModulesUpdated();
            },
            deep: true
        },
    },
    computed: {
        direction() {
            if (isVoid(this.culture)) {
                return cultureManager.getDirection(cultureManager.getActiveCulture().code);
            }
            return cultureManager.getDirection(this.culture);
        }
    }
}
</script>