/**
 *  监听页面动作，维护数据映射及数据集
 *  当前主要使用遍历实现。
 *  @createDate 2019-04-12
 */


/**
 * 遍历映射配置，构建映射关系
 * @param dataMapping   映射配置
 * @param rawData   源数据：&nbsp;&nbsp;&nbsp;&nbsp;若有此输入，将认为当前是更新动作、&nbsp;&nbsp;无此输入则认为当前是插入动作
 */
function mapping(dataMapping, rawData) {
    window.dataSet = {};
    window.dataSet.tempId = 950366;
    window.dataSet.original = JSON.parse(JSON.stringify(rawData));
    if(Object.entries(rawData).length / Object.entries(window.dataMapping).length < 0.5) {
        window.dataSet.isInsert = true;
        var rawData = {};
    }else {
        window.dataSet.isInsert = false;
    }
    let mapper = {}, returnData = {};
    delete dataMapping.mapper;
    Object.entries(dataMapping).forEach((entity) => {
        let beanName = entity[0], propertyNames = entity[1], entityData = rawData[beanName];
        if(!isExist(rawData, beanName)) { return; }
        if(!entityData) { entityData = {}; }
        if(propertyNames instanceof  Array) {
            // Form 时执行此逻辑
            tryForEach(propertyNames,(item) => {
                if(typeof item === "string") {
                    if(!isExist(entityData, item, beanName)) { return; }
                    associate(entityData, item, item, beanName,mapper, returnData);
                    return;
                }  else if(item instanceof Object) {
                    let entry = Object.entries(item)[0];
                    let property = entry[0], alias = entry[1];
                    if(!isExist(entityData, property, beanName)) { return; }
                    associate(entityData, property, alias, beanName,mapper, returnData)
                } else {
                    console.error('The configuration of "'+item+'" in "'+beanName+'" is not recognized,  and the required,' +
                        ' parameters are ( "propertyName" or "{ propertyName: alias }" ).');
                    throw {};
                }
            });
        } else if(propertyNames instanceof Object) {
            // Grid 时执行此逻辑
            if(window.dataSet.isInsert) {
                rawData[beanName] = {};
                rawData[beanName].raw = {};
                rawData[beanName].compares = {};
                rawData[beanName].ids = [];
                return;
            }
            let listMap = {}, ids = [], rowId = ''; //tryForEach 变量作用域注意
            entityData.forEach(rowData => {
                rowId = rowData[propertyNames.id];
                rowData['rowIndex'] = rowId;
                listMap[rowId] = rowData;
                ids.push(rowId);
                settingGridMapper(propertyNames.columns, mapper, rowId, beanName);
            })
            rawData[beanName] = {};
            rawData[beanName].raw = entityData;
            rawData[beanName].compares = listMap;
            rawData[beanName].ids = ids;
            return;
        }
    });
    dataMapping.mapper = mapper;
    // ---------------- 暂不处理无法执行到此处的情况 ----------------
    // 这里写死 映射对象为：‘dataMapping’
    window.dataMapping = dataMapping;
    // 这里写死 数据集：‘dataSet: { rawData: {}, newData: {} }’
    window.dataSet.rawData = rawData;
    window.dataSet.newData = JSON.parse(JSON.stringify(dataSet.rawData));
    return returnData;
}

/**
 * 设置 Grid 映射关系，若是添加行，则需要构建属性
 * @param propertyNames
 * @param mapper
 * @param rowId
 * @param beanName
 * @param data  若是添加行，需要传入 data 构建属性
 */
function settingGridMapper(propertyNames, mapper, rowId, beanName, data) {
    propertyNames.forEach(property => {
        if(!mapper[property]) {
            mapper[property] = {};
        }
        if(!mapper[property][beanName]) {
            mapper[property][beanName] = {};
        }
        mapper[property][beanName][rowId] = {};
        if(data) {
            data[property] = '';
        }
    });
}

/**
 *  映射关系 => elementName(alias): { objName: property }
 * @param data  数据源
 * @param property  属性
 * @param alias 别名
 * @param objName   对象名
 * @param mapper    返回值: 映射关系
 * @param returnData    返回值: 页面显示数据集
 */
function associate(data, property, alias, objName, mapper, returnData) {
    mapper[alias] = {};
    mapper[alias][objName] = property;
    returnData[alias] = data[property];
}

function testTryForEach() {
    // 直接修改原型属性， 会导致当前UI grid 组件 input 不可用，未知原因。
    Array.prototype.tryForEach = function (callback) {
        try {
            this.forEach(callback);
        } catch { }
    }
}

const tryForEach = function(arrayObj, callback) {
    try {
        arrayObj.forEach(callback);
    } catch(error) {
        console.error(error);
    }
}

/**
 * 检测数据中是否存在对应属性
 * @param obj   需要检测数据
 * @param property  需要检测的属性
 * @param objName   检测的对象名
 * @returns {boolean}
 */
function isExist(obj, property, objName) {
    if(window.dataSet.isInsert) {
        obj[property] = {};
        return true;
    } else {
        if(!objName) { objName = 'rawData'; }
        // 这里注意，后端返回的类名是否与映射配置一致
        if(obj[property] === undefined) {
            console.error('No data for name of ' + property + ' in ' + objName + ' !');
            return false;
        }
    }
    return true;
}

/**
 *  检查是否存在此 Grid 的映射 返回匹配到的映射
 * @param str   Grid Id
 * @returns {{}}
 */
function checkArrayMapper(str) {
    let retrunData = {};
    Object.entries(window.dataMapping).forEach(entry => {
        let beanName = entry[0], property = entry[1];
        if(str.indexOf(beanName) >= 0) {
            retrunData['beanName'] = beanName;
            retrunData['property'] = property;
        }
    });
    return retrunData;
}

/**
 * Grid 编辑前触发。 这里记录当前编辑框 对应的 Grid 行索引 GridRowIndex
 * @param options
 * @private
 */
function onBeforeEdit_(options) {
    const originalOnBeforeEdit = options.onBeforeEdit;
    options.onBeforeEdit = function(editParm, gg){
        let domIds = editParm.column.__domid.split('|'), recordId = editParm.record.__id, colName = editParm.column.name;
        let parentDomId = domIds[0]+'|'+2+'|'+recordId; // +'|'+domIds[2];
        let rowDOM;
        if(!(rowDOM = document.getElementById(parentDomId))) {
            parentDomId = domIds[0]+'|'+1+'|'+recordId;
            rowDOM = document.getElementById(parentDomId)
        }
        let mapper = window.dataMapping.mapper;
        let rowId = rowDOM.childNodes[0].childNodes[0].innerText;
        let mapperEntry =  mapper[colName];
        let currentMapper = checkArrayMapper(domIds[0]);
        if(currentMapper.hasOwnProperty("beanName")) {
            if(!mapperEntry[currentMapper.beanName]) {
                console.error("dataMapping 中，无法找到该 DOMId 的映射。请检查！");
            } else {
                //mapperEntry[currentMapper.beanName]['rowId'] = parentDomId;
                mapperEntry[currentMapper.beanName][rowId]['rowIndex'] = editParm.rowindex;
            }
        }
        originalOnBeforeEdit.call(this, editParm, '11');
    }
}

function listeningSearchbox(elId, options) {
    const originalOnChange = options.onChange;
    options.onChange = function(value, data) {
        let eleName = this.element[0].name;
        let currentMapper =  window.dataMapping.mapper[eleName];
        if(!currentMapper) {
            console.error("找不到该映射,请检查映射配置是否存在该配置: "+ eleName);
        }
        let fillInValue;
        Object.entries(currentMapper).forEach(mapper => {
            let property = "";
            if(eleName.split('_').length > 1) {
                property = eleName.split('_')[1];
            } else {
                property = mapper[1];
            }
            let beanName = mapper[0];

            window.dataSet.newData[beanName][property] = data[property];
            fillInValue = data[property];
        })
        originalOnChange.call(this, value, data, fillInValue);
    }
    $(elId).searchbox(options);
}

/**
 *  若需要监听 Grid 数据，必须在此调用组件初始化，此处获取元素 ID
 * @param elId
 * @param options
 * @returns {*}
 */
function listeningGrid(elId, options) {
    let currentMapper = checkArrayMapper(elId);

    if(!currentMapper.property) {
        console.error("若需要监听 GridData ,必须在映射配置指定 ID ！请检查。");
        return;
    }
    options.columns.unshift({ name: 'rowIndex', hide: true });
    debugger;
    let data = {};
    if(!options.data){
        console.error('listeningGrid 必须传入 Data ！请检查');
        data.Rows = '';
    } else { data.Rows = options.data.raw; }
    options.data = data;
    // ------------------ 编辑框打开前记录当前行 ------------------
    onBeforeEdit_(options);

    const toolbar = options.toolbar;
    if(toolbar) {
        toolbar.items
        tryForEach(toolbar.items, but => {
            // ------------------ Grid 新增行时，设置 mapper、 newData -----------------
            if(but.id === "menuAdd") {
                // 使用 gridManager 对象 可以省略 页面监听操作。需要修改。
                const originalOnClick = but.click;
                but.click = function() {
                    debugger;
                    let currentMapper = checkArrayMapper(elId);
                    let beanName = currentMapper.beanName;
                    let idProperty = currentMapper.property.id;
                    if(!idProperty) {
                        console.error("若需要监听 GridData ,必须在映射配置指定 ID ！请检查。");
                        throw {};
                    }
                    let mapper = window.dataMapping.mapper;
                    let rowId = window.dataSet.tempId += 1
                    let data = {}, mapperData = {};
                    // ------------- 设置 mapper -------------
                    settingGridMapper(currentMapper.property.columns, mapper, rowId, beanName, data);

                    data[idProperty] = rowId;
                    data['rowIndex'] =rowId
                    // ------------- 设置 compares -------------
                    if(!window.dataSet.newData[beanName]) {
                        window.dataSet.newData[beanName] = {};
                    }
                    if(!window.dataSet.newData[beanName].compares){
                        window.dataSet.newData[beanName].compares = {};
                    }
                    window.dataSet.newData[beanName].compares[rowId] = data;
                    // ------------- 设置 ids -------------
                    if(!window.dataSet.newData[beanName].ids) {
                        window.dataSet.newData[beanName].ids = [];
                    }
                    window.dataSet.newData[beanName].ids.push(rowId);
                    UICtrl.addGridRow(gridManager, data);
                    originalOnClick.call(this, data);
                }
            }
            if(but.id === "menuDelete") {
                const originalOnClick = but.click;
                but.click = function() {
                    let selectRow = gridManager.getSelectedRow();
                    let currentMapper = checkArrayMapper(gridManager.id);
                    let beanName = currentMapper.beanName;
                    let currentData = window.dataSet.newData[beanName];
                    let propertyId = window.dataMapping[beanName].id;
                    let currentId = selectRow[propertyId] + '';
                    delete currentData.compares[currentId];
                    currentData.ids = currentData.ids.filter(x => !currentId.includes(x));

                    currentMapper.property.columns.forEach(property => {
                        delete window.dataMapping.mapper['productFaceColorId'][beanName][currentId];
                    });
                    gridManager.deleteSelectedRow();
                    debugger;
                    originalOnClick.call(this, selectRow);
                }
            }
        })
    }
    addObserver(elId);
    var gridManager = UICtrl.grid(elId,  options);
    return gridManager;
}

/**
 * 监听 input 管理页面显示的 Data。
 */
function inputEventListener() {
    const inputElements = document.querySelectorAll('input');
    inputElements.forEach(item => {
        item.addEventListener('input', (event) => {
            listenerContent(event);
        });
    });
}

/**
 * 监听器动作： 根据 保存的映射关系，将页面元素值填入到 newData
 * @param event
 */
function  listenerContent(event) {
    const property = event.target.getAttribute('name'), editId = event.target.id;
    const beanMapping = window.dataMapping.mapper[property];
    if(!beanMapping) {
        console.error("The configuration was not found in the 'dataMapping' : '"+property+"'")
    }
    let control;
    let inputIds = event.target.id.split('_');
    let currentMapper = checkArrayMapper(inputIds[0]);
    if (currentMapper.property) {
        control = 'grid';
    } else {
        control = 'form';
    }
    // Grid 时执行此逻辑
    if(control === 'grid') {
        debugger;
        tryForEach(Object.entries(beanMapping), mapperEntry => {
            let mapperKey = mapperEntry[0], mapperVal = mapperEntry[1];

            Object.entries(mapperEntry[1]).forEach(rowEntry => {
                let rowId = rowEntry[0];
                if(rowEntry[1].rowIndex != undefined) {
                    if(rowEntry[1].rowIndex === inputIds[2] * 1) {
                        window.dataSet.newData[mapperKey].compares[rowId][inputIds[1]] = event.target.value;
                        console.log('Input value changed:', event.target.value);
                    }
                }
            })
        })
        return;
    }
    // Form 时执行此逻辑
    if(control === 'form') {
        let mappings = Object.entries(beanMapping);
        tryForEach(mappings, (entry) => {
            let beanName = entry[0];
            let property = entry[1];
            if(event.target.type === 'checkbox') {
                window.dataSet.newData[beanName][property] = event.target.checked ? 1 : 0;
                console.log('checked value changed:', event.target.checked);
            } else {
                window.dataSet.newData[beanName][property] = event.target.value;
                console.log('Input value changed:', event.target.value);
            }

        })
    }
}

// function getControlType(beanMapping) {
//     let control;
//     //  这里根据 grid 与 form 的 mapper 数据结构差异判定。 需要优化
//     debugger;
//     if(Object.entries(beanMapping)[0][1]['listeningKey']) {
//         control = 'grid';
//     } else {
//         control = 'form';
//     }
//     return control;
// }

/**
 * 添加 DOM 监听
 * @param eleId 监听的 root 元素 ID
 */
function addObserver(eleId) {
    observer.observe(document.querySelector(eleId), mutationConfig);
}

/**
 * 监听 DOM ，为编辑框添加 Input 监听
 * @type {MutationObserver}
 */
const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
        if(mutation.type === 'childList') {
            let addNodes = mutation.addedNodes;
            if(addNodes.length === 1 && addNodes[0].nodeName === 'DIV') {
                let childNodes = addNodes[0].childNodes;
                if(childNodes.length === 1 && childNodes[0].nodeName === 'INPUT') {
                    let node = childNodes[0];
                    node.addEventListener('input', gridEditor)
                }
            }
            let removeNodes = mutation.removedNodes;
            if(removeNodes.length === 1 && removeNodes[0].nodeName === 'INPUT') {
                let node = removeNodes[0];
                node.removeEventListener('input', gridEditor);
            }
        }
    })
})

const gridEditor = function(event) {
    listenerContent(event);
}
// console.log('属性变化节点 ： ' + mutation.target);
// console.log('变化属性名 ： ' + mutation.attributeName);
// console.log('变化属性值 ： ' + mutation.target.value);
const mutationConfig = { attributes: true, childList: true, subtree: true}

function checkUpdata() {
    let dataSet = window.dataSet;
    let rawData = dataSet.rawData, newData = dataSet.newData;
    window.dataSet.upData = {};
    let rawEntry = Object.entries(rawData);
    debugger;
    tryForEach(rawEntry, entry => {
        let beanName = entry[0];
        console.log(beanName);
        let mapping = window.dataMapping[beanName], raw = rawData[beanName], now = newData[beanName];
        if(mapping instanceof Array) {
            // Form 时 执行此逻辑
            if(!(getHashCode(JSON.stringify(raw)) === getHashCode(JSON.stringify(now)))) {
                dataSet.upData[beanName] = newData[beanName];
            }
            return;
        } else if (mapping instanceof Object) {
            // Grid 时 执行此逻辑
            let rawCompares = raw.compares, nowCompares = now.compares;
            if(!rawCompares) { rawCompares = {}; }
            if(!nowCompares) { nowCompares = {}; }
            if(!(getHashCode(JSON.stringify(rawCompares)) === getHashCode(JSON.stringify(nowCompares)))) {
                let rawIds = raw.ids, nowIds = now.ids;
                dataSet.upData[beanName] = {};
                dataSet.upData[beanName].del = [];
                let delIds = rawIds.filter(x => !nowIds.includes(x));
                tryForEach(delIds, id => {
                    dataSet.upData[beanName].del.push(rawCompares[id]);
                });
                dataSet.upData[beanName].add = []
                let addIds = nowIds.filter(x => !rawIds.includes(x));
                tryForEach(addIds, id => {
                    dataSet.upData[beanName].add.push(nowCompares[id]);
                });
                dataSet.upData[beanName].up = []
                let nowEntry = Object.entries(nowCompares);
                tryForEach(nowEntry, row => {
                    let key = row[0], rowNow = row[1];
                    if(rawCompares[key]) {
                        if(!(getHashCode(JSON.stringify(rawCompares[key])) === getHashCode(JSON.stringify(rowNow)))) {
                            dataSet.upData[beanName].up.push(rowNow);
                        }
                    }
                });
            }
            return ;
        }
    })
    return dataSet.upData;
}

/**
 * 用于判断字符串是否更改
 * @param str
 * @param caseSensitive 是否区分大小写 Default: true
 * @returns {number}
 */
function getHashCode (str, caseSensitive) {
    if(caseSensitive === undefined) { caseSensitive = true;}
    if(!caseSensitive) {
        str = str.toLowerCase();
    }
    if (str.length === 0) return 0;
    var len = str.length;
    var hash = 1315423911, i, ch;
    for (i = len - 1; i >= 0; i--) {
        ch = str.charCodeAt(i);
        hash = (hash << 5) + ch + (hash >> 2);
    }
    var hash2 = hash ^ (hash << 7) ^ (hash >> 6);
    var hash3 = hash2 ^ (hash2 << 1) ^ (hash2 >> 13);
    if (len < 50) {
        var hash4 = this.getHashCode(str + str + str, caseSensitive);
        return (hash ^ hash2 ^ hash3  ^ hash4) & 0x7FFFFFFF;
    } else {
        return (hash ^ hash2 ^ hash3 >> 3 ) & 0x7FFFFFFF;
    }
}
