
function defaultEditGrid(element, paramColumns, toolbarHtml, itemLevelColumnName, layoutData, screenMargin, flexColumns) {
    let jexcelInstance;
    let columns = paramColumns;
    let columnNames = [];
    for (let i = 0; i < paramColumns.length; i++) {
        columnNames[i] = paramColumns[i].name;
    }
    const itemLevelColumnIndex = columnNames.indexOf(itemLevelColumnName)

    function stringToColour(stringInput, alpha) {
        let stringUniqueHash = [...stringInput].reduce((acc, char) => {
            return char.charCodeAt(0) + ((acc << 5) - acc);
        }, 0);
        return `hsla(${stringUniqueHash % 360}, 85%, 40%, ${alpha})`;
    }

    var customUpdateTable = function(instance, cell, col, row, val, label, cellName) {
        if(col == 0 && val != null && val != ''){
            //cell.style.backgroundColor = '#f46e42';
            cell.style.textAlign = 'left';
            var rowData = jexcelInstance.getRowData(Number(row));
            if(rowData.length > 1){
                // const itemLenIndex = columnNames.indexOf('itemLevel');
                const depth = Number(rowData[itemLevelColumnIndex]);
                cell.style.textAlign = 'left';
                const padding = depth == 1 ? 5 : (depth*10);
                cell.style.paddingLeft = padding+'px';
                const colorVal = (padding*2)+'';
                const fromColor = stringToColour(colorVal, '20%');
                const toColor = stringToColour(colorVal, '60%');
                cell.style.background = `linear-gradient(to right, ${fromColor}, ${fromColor}, ${toColor}) no-repeat left`;
                cell.style.backgroundSize = (padding - 2) +'px 2px';
            }
        }else{
            const columnProp = columns[col];
            if (columnProp.align != null) {
                cell.style.textAlign = columnProp.align;
            }
        }
    }

    //JEXCEL 관련 함수 시작
    function getCellValue(columnName, rowNum){
        const index = columnNames.indexOf(columnName);
        //var cell = gridInstance.records[rowNum][index];
        //return cell.innerText;
        const cellId = String.fromCharCode(65+index) + rowNum;
        return jexcelInstance.getValue(cellId)
    }

    const beforePaste = function (instance, data){
        console.log('beforePaste');

        const selectedCell = jexcelInstance.selectedCell;
        const obj = jexcelInstance;

        var ax = Number(selectedCell[0]),
            ay = Number(selectedCell[1]),
            bx = Number(selectedCell[2]),
            by = Number(selectedCell[3]);

        // Paste filter
        var x = ax,
            y = ay,
            w = bx-x+1,
            h = by-y+1;

        // change paste range if select is from right to left
        if (bx < ax){
            x = bx;
            w = ax-x+1;
        }
        // change paste range if select is from down to up
        if (by < ay){
            y = by;
            h = ay-y+1;
        }

        // Controls
        var hash = obj.hash(data);
        var style = (hash == obj.hashString) ? obj.style : null;

        // Depending on the behavior
        if (obj.options.copyCompatibility == true && hash == obj.hashString) {
            var data = obj.data;
        }

        // Split new line
        var data = obj.parseCSV(data, "\t");

        //modify data to allow wor extending paste range in multiples of input range
        if (w>1 & Number.isInteger(w/data[0].length )){
            style = null;
            repeats = w/data[0].length;

            var arrayB = data.map(function(row,i){
                var arrayC = Array.apply(null, {length: repeats * row.length})
                    .map(function(e,i){return row[i % row.length]});
                return arrayC
            });
            data = arrayB;

        }
        if (h>1 & Number.isInteger(h/data.length )){
            style = null;
            var repeats = h/data.length;
            var arrayB = Array.apply(null, {length: repeats * data.length})
                .map(function(e,i){return data[i % data.length]});
            data = arrayB;
        }

        if (x != null && y != null && data) {
            // Records
            var i = 0;
            var j = 0;
            var records = [];
            var newStyle = {};
            var oldStyle = {};
            var styleIndex = 0;

            // Index
            var colIndex = parseInt(x);
            var rowIndex = parseInt(y);
            var row = null;

            // Go through the columns to get the data
            while (row = data[j]) {
                i = 0;
                var colIndex = parseInt(x);

                while (row[i] != null) {
                    // Update and keep history
                    var record = obj.updateCell(colIndex, rowIndex, row[i]);
                    // Keep history
                    records.push(record);
                    // Style
                    if (style) {
                        var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]);
                        newStyle[columnName] = style[styleIndex];
                        oldStyle[columnName] = obj.getStyle(columnName);
                        obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]);
                        styleIndex++
                    }
                    i++;
                    if (row[i] != null) {
                        if (colIndex >= obj.headers.length - 1) {
                            obj.insertColumn();
                        }
                        colIndex = obj.right.get(colIndex, rowIndex);
                    }
                }

                j++;
                if (data[j]) {
                    if (rowIndex >= obj.rows.length - 1) {
                        obj.insertRow();
                    }
                    var rowIndex = obj.down.get(x, rowIndex);
                }
            }

            // Select the new cells
            obj.updateSelectionFromCoords(x, y, colIndex, rowIndex);

            // Update history
            obj.setHistory({
                action: 'setValue',
                records: records,
                selection: obj.selectedCell,
                newStyle: newStyle,
                oldStyle: oldStyle,
            });

            // Update table
            obj.updateTable();
        }
        return false;
    };

    function moveSelectionRow(up){
        const selectedCell = jexcelInstance.selectedCell;

        const correction = up ? -1 : 1 ;
        const selectionCorrection = up ? 0 : 2 ;

        if(selectedCell != null) {
            const x1 = Number(selectedCell[0]) ;
            const y1 = Number(selectedCell[1]) ;
            const x2 = Number(selectedCell[2]);
            const y2 = Number(selectedCell[3]);
            if(up){
                if(y1 === 0)
                    return;
                jexcelInstance.moveRow(y1 + correction, y2);
            }else{
                const rowSize = jexcelInstance.getData().length;
                if(y2 + 1 === rowSize)
                    return;
                jexcelInstance.moveRow(y2 + correction, y1);
            }

            const CODE_A = 65;
            const x1Code = String.fromCharCode(CODE_A+x1);
            const x2Code = String.fromCharCode(CODE_A+x2);
            const startCellCode = x1Code + (y1+selectionCorrection);
            const endCellCode = x2Code + (y2+selectionCorrection);

            jexcelInstance.updateSelection(jexcelInstance.getCell(startCellCode), jexcelInstance.getCell(endCellCode));
        }
    }

    function updateSelectedDepth(increse){
        const selectedCell = jexcelInstance.selectedCell;
        const depthColumn = itemLevelColumnIndex;

        if(selectedCell != null) {
            const y1 = Number(selectedCell[1]);
            const y2 = Number(selectedCell[3]);
            const top = y1 < y2 ? y1 : y2;
            const gap = (y1 < y2 ? y2 - y1 : y1 - y2) + 1;

            const values = [];
            for (let i = 0; i < gap; i++) {
                const cellNum = 'C'+(top+i+1);
                let value = Number(jexcelInstance.getValue(cellNum));
                value = increse ? value + 1 : (value === 1 ? 1 : value - 1);
                values[i] = String(value);
            }
            updateValues(top, depthColumn, values);
        }
    }

    // function updateSelectedColumn(colIndex, value){
    //     const selectedCell = jexcelInstance.selectedCell;
    //     if(selectedCell != null) {
    //         const y1 = Number(selectedCell[1]);
    //         const y2 = Number(selectedCell[3]);
    //         const top = y1 < y2 ? y1 : y2;
    //         const gap = (y1 < y2 ? y2 - y1 : y1 - y2) + 1;
    //
    //         const values = [];
    //         for (let i = 0; i < gap; i++) {
    //             values[i] = value;
    //         }
    //         updateValues(top, colIndex, values);
    //     }
    // }

    function updateValues(x, y, values){
        var records = [];
        for (let i = 0; i < values.length; i++) {
            const rowIndex = x + i;
            // Update and keep history
            var record = jexcelInstance.updateCell(y, rowIndex, values[i]);
            // Keep history
            records.push(record);
        }

        // Update history
        jexcelInstance.setHistory({
            action: 'setValue',
            records: records,
            selection: jexcelInstance.selectedCell,
        });

        // Update table
        jexcelInstance.updateTable();
    }

    function onTabKey(event){
        const selectedCell = jexcelInstance.selectedCell;
        const x = Number(selectedCell[0]) + 1;
        const y = Number(selectedCell[1]) + 1;
        const rowSize = jexcelInstance.getData().length;
        const colSize = columnNames.length;

        if(x === colSize){
            if(y === rowSize){
                jexcelInstance.insertRow();
            }
            const cellNum = 'A'+(y+1);
            jexcelInstance.updateSelection(jexcelInstance.getCell(cellNum));
            event.preventDefault();
            event.stopPropagation();
        }
        console.log(x+','+y+','+rowSize+','+colSize);
    }

    function jexcelContextMenu (obj, x, y, e) {
        var items = [];

        if (y == null) {
            // Insert a new column
            if (obj.options.allowInsertColumn == true) {
                items.push({
                    title: obj.options.text.insertANewColumnBefore,
                    onclick: function () {
                        obj.insertColumn(1, parseInt(x), 1);
                    }
                });
            }

            if (obj.options.allowInsertColumn == true) {
                items.push({
                    title: obj.options.text.insertANewColumnAfter,
                    onclick: function () {
                        obj.insertColumn(1, parseInt(x), 0);
                    }
                });
            }

            // Delete a column
            if (obj.options.allowDeleteColumn == true) {
                items.push({
                    title: obj.options.text.deleteSelectedColumns,
                    onclick: function () {
                        obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x));
                    }
                });
            }

            // Rename column
            if (obj.options.allowRenameColumn == true) {
                items.push({
                    title: obj.options.text.renameThisColumn,
                    onclick: function () {
                        obj.setHeader(x);
                    }
                });
            }

            // Sorting
            if (obj.options.columnSorting == true) {
                // Line
                items.push({type: 'line'});

                items.push({
                    title: obj.options.text.orderAscending,
                    onclick: function () {
                        obj.orderBy(x, 0);
                    }
                });
                items.push({
                    title: obj.options.text.orderDescending,
                    onclick: function () {
                        obj.orderBy(x, 1);
                    }
                });
            }
        } else {
            // Insert new row
            if (obj.options.allowInsertRow == true) {
                items.push({
                    title: '행 삽입',
                    onclick: function () {
                        const selectedCell = obj.selectedCell;
                        if (selectedCell == null) {
                            obj.insertRow(1, parseInt(y), 1);
                        } else {
                            const y1 = Number(selectedCell[1]);
                            const y2 = Number(selectedCell[3]);
                            const top = y1 < y2 ? y1 : y2;
                            const gap = (y1 < y2 ? y2 - y1 : y1 - y2) + 1;
                            obj.insertRow(gap, top, 1);
                        }
                    }
                });

                items.push({
                    title: 'Add Row',
                    onclick: function () {
                        const selectedCell = obj.selectedCell;
                        if (selectedCell == null) {
                            obj.insertRow(1, parseInt(y), 1);
                        } else {
                            const y1 = Number(selectedCell[1]);
                            const y2 = Number(selectedCell[3]);
                            const bottom = y1 > y2 ? y1 : y2;
                            const gap = (y1 < y2 ? y2 - y1 : y1 - y2) + 1;
                            obj.insertRow(gap, bottom);
                        }
                    }
                });
            }

            if (obj.options.allowDeleteRow == true) {
                items.push({
                    title: 'Delete Row',
                    onclick: function () {
                        obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y));
                    }
                });
            }
        }

        // Line
        items.push({type: 'line'});

        // Copy
        items.push({
            title: obj.options.text.copy,
            shortcut: 'Ctrl + C',
            onclick: function () {
                obj.copy();
            }
        });
        // Paste
        items.push({
            title: obj.options.text.paste,
            shortcut: 'Ctrl + V',
            onclick: function () {
                obj.paste();
            }
        });
        // Line
        items.push({type: 'line'});
        items.push({
            title: '깊이',
            submenu: [
                {
                    title: '깊이증가',
                    shortcut: 'Alt + →',
                    onclick: function () {
                        updateSelectedDepth(true);
                    }
                },
                {
                    title: '깊이감소',
                    shortcut: 'Alt + ←',
                    onclick: function () {
                        updateSelectedDepth(false);
                    }
                },
            ]
        });
        items.push({
            title: '행 이동',
            submenu: [
                {
                    title: '위로이동',
                    shortcut: 'Alt + ↑',
                    onclick: function () {
                        moveSelectionRow(true);
                    }
                },
                {
                    title: '아래로이동',
                    shortcut: 'Alt + ↓',
                    onclick: function () {
                        moveSelectionRow(false);
                    }
                },
            ]
        });

        return items;
    }

    function createEditGrid(element, columns, toolbarHtml, layoutData) {

        jexcelInstance = jexcel(element.get(0), {
            data: [[]],
            columns: columns,
            rowResize: false,
            rowDrag: true,
            columnResize: false,
            columnDrag: false,
            columnSorting: false,
            autoIncrement: false,
            allowInsertColumn: false,
            allowInsertOnTab: true,
            tableOverflow: true,
            allowManualInsertRow: false,
            tableHeight: 2500,
            onbeforepaste: beforePaste,
            allowDeletingAllRows: true,
            updateTable: customUpdateTable,
            contextMenu: jexcelContextMenu
        });

        const superSetHistory = jexcelInstance.setHistory;
        jexcelInstance.setHistory = function(changes){
            if("setWidth" === changes.action){
                return ;
            }
            superSetHistory(changes);
        };

        element.on("keydown", function (e) {
            if (e.altKey) {
                if (e.keyCode == 39) {
                    updateSelectedDepth(true);
                    return false;
                }
                if (e.keyCode == 37) {
                    updateSelectedDepth(false);
                    return false;
                }
                if (e.keyCode == 38) {
                    moveSelectionRow(true);
                    return false;
                }
                if (e.keyCode == 40) {
                    moveSelectionRow(false);
                    return false;
                }
            }
            if (!e.shiftKey && e.keyCode == 9) {
                onTabKey(e);
            }
        });

        if(toolbarHtml != null && toolbarHtml != ''){
            element.children().first().before(toolbarHtml);
        }
        const gridInstanceData = [[]];
        for (let i = 0; i < layoutData.length; i++) {
            const row = layoutData[i];
            gridInstanceData[i] = row;
        }
        jexcelInstance.setData(gridInstanceData);
        resizeGridInstance();

        return jexcelInstance;
    }

    let resizing = false;

    function resizeGridInstance(){
        let screenGap = screenMargin == null ? 113 : screenMargin;
        var screenWidth = $(window).width() - screenGap;
        var columnWidth = 0;
        columns.forEach( function (element){
            columnWidth += element.width;
        } );
        var gap = screenWidth - columnWidth;
        if(flexColumns == null) {
            var halfGap = gap / 2;
            jexcelInstance.setWidth(0, Number(columns[0].width) + halfGap);
            jexcelInstance.setWidth(1, Number(columns[1].width) + halfGap);
        }else{
            const cnt = flexColumns.length
            const columnGap = gap / cnt;
            for (let flexColumn of flexColumns) {
                const index = columnNames.indexOf(flexColumn);
                jexcelInstance.setWidth(index, Number(columns[index].width) + columnGap);
            }
        }
        resizing = false;
    }

    $( window ).resize( function() {
        if(!resizing){
            resizing = true;
            setTimeout(resizeGridInstance, 10);
        }
    })

    return createEditGrid(element, paramColumns, toolbarHtml, layoutData);
}