import Vue from "vue";
import utils from "@/utils.js";
import FormMath from "@/math.js";
import hljs from "highlight.js";

const directive = {};

directive.install = (Vue, opts = {}) => {
    // 代码高亮指令
    Vue.directive('highlight', {
        componentUpdated: function (el) {
            setTimeout(() => {
                let blocks = el.querySelectorAll('pre code');
                if (blocks && blocks.constructor === Array) {
                    blocks.forEach((block) => {
                        hljs.highlightBlock(block)
                    })
                }
            }, 1);
        }
    });

    // 表单tr中根据隐藏字段动态合并单元格的指令
    Vue.directive('permit', function (el, binding) {
        if (el.tagName !== "TR") {
            throw "v-permit指令只能用在tr元素上.";
        }
        if (!binding.value || binding.value.constructor !== Number) {
            throw "v-permit指令的值只能是大于0的数字.";
        }
        el.removeAttribute("hidden");

        // tr中没有子元素时，删除tr自身
        if (el.cells.length == 0) {
            el.setAttribute("hidden", "hidden");
        }
        else if (el.cells.length < binding.value) {
            let colspan = binding.value - el.cells.length + 1;
            // 设置colspan实现单元格合并
            el.cells[el.cells.length - 1].setAttribute("colspan", colspan);
        }
        else if (el.cells.length == binding.value) {
            for (var i = 0, c; c = el.cells[i++];) {
                c.removeAttribute("colspan");
            }
        }
    });

    // 表单指令，会在表单的vue实例上添加一个map，用以存放数学运算的表达式
    Vue.directive('form', {
        bind: function (el, binding, vnode) {
            // 修正vee-validate中对于元素上的aria-invalid属性更新的问题
            const judgeAriaInvalid = (fields) => {
                if (!fields || fields.length == 0) {
                    return;
                }

                const fieldInst = fields.items[0];
                const includes = function (collection, item) {
                    return collection.indexOf(item) !== -1;
                };
                const isCheckboxOrRadioInput = function (el) {
                    return includes(["radio", "checkbox"], el.type);
                };
                const isCallable = function (func) {
                    return typeof func === "function";
                };
                Object.getPrototypeOf(fieldInst).constructor.prototype.updateAriaAttrs = function updateAriaAttrs() {
                    var this$1 = this;

                    if (!this.aria || !this.el || !isCallable(this.el.setAttribute)) {
                        return;
                    }

                    var applyAriaAttrs = function (el) {
                        el.setAttribute("aria-required", this$1.isRequired ? "true" : "false");
                        el.setAttribute("aria-invalid", this$1.flags.invalid ? "true" : "false");
                        // 组件初次加载时，既不显示验证失败也不显示验证成功
                        if (this$1.flags.invalid == null) {
                            el.removeAttribute("aria-invalid");
                        }
                    };

                    if (!isCheckboxOrRadioInput(this.el)) {
                        applyAriaAttrs(this.el);
                        return;
                    }

                    var els = document.querySelectorAll('input[name="' + this.el.name + '"]');
                    toArray(els).forEach(applyAriaAttrs);
                }
            }

            const inst = vnode.context;

            inst.$root.$validator && judgeAriaInvalid(inst.$root.$validator.fields);
            if (inst && !inst.hasOwnProperty("watchMap")) {
                inst["watchMap"] = new Map();
            }
        }
    });

    // 判断输入框是否作为计算字段，是则添加监听
    Vue.directive('express', {
        componentUpdated: function (el, binding, vnode) {
            const inst = vnode.context;
            if (!inst.$vnode.data.model) {
                return;
            }
            let elAttr = inst.$vnode.data.model.expression;

            if (inst.$vnode && inst.$vnode.componentOptions && inst.$vnode.componentOptions.propsData && inst.$vnode.componentOptions.propsData.modelExpression) {
                elAttr = inst.$vnode.componentOptions.propsData.modelExpression;
            }

            if (elAttr && !inst._expressInit) {
                inst._expressInit = true;
                // 子表每一行数据作用域所在的dom元素
                let { subScopeEl } = utils.getSubScopeElAndIndex(el);
                let subname = null;
                let aliasElAttr = null;
                // 子表数据，需要找到配置了data-subname的元素
                if (subScopeEl) {
                    subname = subScopeEl.dataset["subname"];
                    if (!subname) {
                        throw ("无法获取到当前子表前缀");
                    }
                    const elAttrAry = elAttr.split("."), elAttrArySize = elAttrAry.length;
                    if (elAttrArySize < 1) {
                        throw `子表中的数据绑定表达式${elAttr}错误`;
                    }
                    aliasElAttr = `${subname}.${elAttrAry[elAttrArySize - 1]}`;
                }
                const p = utils.getOnlineFormInstance(inst);
                if (p.watchMap && (p.watchMap.has(elAttr) || p.watchMap.has(aliasElAttr))) {
                    if (!inst._watchers.some(m => m.expression === 'inputVal')) {
                        inst.$watch("inputVal", function (newVal, oldVal, farewell) {
                            if (newVal !== oldVal) {
                                const elAttrExpList = p.watchMap.get(elAttr) || [];
                                const aliasElAttrExpList = p.watchMap.get(aliasElAttr) || [];
                                const unionExpList = elAttrExpList.concat(aliasElAttrExpList);
                                const attrExpObj = {};
                                unionExpList.forEach(item => {
                                    let result = 0;
                                    const t = item.target;
                                    const targetList = attrExpObj[t] || [];
                                    if (targetList.indexOf(item.exp) < 0) {
                                        targetList.push(item.exp);
                                        attrExpObj[t] = targetList;
                                        if (subScopeEl && t.split(".").length == 2) {
                                            const index = subScopeEl.dataset["index"];
                                            if (index === undefined) {
                                                throw ("获取不到当前子表行的索引，无法完成计算.");
                                            }
                                            result = FormMath.calcSubExpValue(item.exp, p, subname, index);
                                            p.$emit(t, { result, index });
                                        }
                                        else {
                                            result = FormMath.calcExpValue(item.exp, p);
                                            p.$emit(t, { result });
                                        }
                                    }
                                });
                            }
                        }, { immediate: true });
                    }
                }
            }
        }
    });

    // 注册一个过滤器，将byte单位的数值转换为合适单位的数值返回
    Vue.filter("computerSize", function renderSize(value) {
        if (null == value || value == '') {
            return "0 Bytes";
        }
        var unitArr = new Array("Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
        var index = 0;
        var srcsize = parseFloat(value);
        index = Math.floor(Math.log(srcsize) / Math.log(1024));
        var size = srcsize / Math.pow(1024, index);
        size = size.toFixed(2);//保留的小数位数
        return size + unitArr[index];
    });

    Vue.filter('dateformat', function (dataStr, pattern = 'yyyy-MM-dd hh:mm:ss') {
        if (dataStr) {
            if (pattern == 'yyyy-MM-dd hh:mm:ss') {
                pattern = 'YYYY-MM-DD HH:mm:ss';
            }
            return new Date(dataStr).format(pattern)
        } else {
            return "";
        }

    });
}

if (typeof window !== 'undefined' && window.Vue) {
    directive.install(window.Vue);
}

export default directive;