import Store from "../store";
import { replaceHtml, getObjType, chatatABC, luckysheetactiveCell } from "../utils/util";
import { getSheetIndex, getluckysheet_select_save, getluckysheetfile } from "../methods/get";
import locale from "../locale/locale";
import method from './method';
import formula from './formula';
import func_methods from "./func_methods";
import tooltip from "./tooltip";
import json from "./json";
import editor from "./editor";
import luckysheetformula from './formula';
import exportSheetExcel from './../utils/exportSheet';
import cleargridelement from './cleargridelement';
import { genarate, update } from './format';
import { setAccuracy, setcellvalue } from "./setdata";
import { orderbydata } from "./sort";
import { rowlenByRange } from "./getRowlen";
import { getdatabyselection, getcellvalue } from "./getdata";
import { luckysheetrefreshgrid, jfrefreshgrid, jfrefreshgrid_rhcw } from "./refresh";
import { luckysheetDeleteCell, luckysheetextendtable, luckysheetdeletetable } from "./extend";
import { isRealNull, valueIsError, isRealNum, isEditMode, hasPartMC } from "./validate";
import { isdatetime, diff } from "./datecontroll";
import { getBorderInfoCompute } from './border';
import { luckysheetDrawMain } from './draw';
import pivotTable from '../controllers/pivotTable';
import server from "../controllers/server";
import menuButton from '../controllers/menuButton';
import selection from "../controllers/selection";
import luckysheetConfigsetting from "../controllers/luckysheetConfigsetting";
import luckysheetFreezen from "../controllers/freezen";
import luckysheetsizeauto from '../controllers/resize';
import sheetmanage from '../controllers/sheetmanage';
import conditionformat from '../controllers/conditionformat';
import { luckysheet_searcharray } from "../controllers/sheetSearch";
import { selectHightlightShow, selectIsOverlap } from '../controllers/select';
import { sheetHTML, luckysheetdefaultstyle } from '../controllers/constant';
import { createFilterOptions } from '../controllers/filter';
import controlHistory from '../controllers/controlHistory';
import { zoomRefreshView, zoomNumberDomBind } from '../controllers/zoom';
import dataVerificationCtrl from "../controllers/dataVerificationCtrl";
import imageCtrl from '../controllers/imageCtrl';
import dayjs from "dayjs";
import { getRangetxt } from '../methods/get';
import { luckysheetupdateCell } from '../controllers/updateCell';
const IDCardReg = /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;

/**
 * 获取单元格的值
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {String} options.type 单元格的值类型，可以设置为原始值"v"或者显示值"m"；默认值为'v',表示获取单元格的实际值
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getCellValue(row, column, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('Arguments row or column cannot be null or undefined.', '')
    }
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        type = 'v',
        order = curSheetOrder
    } = { ...options };
    let targetSheetData = Store.luckysheetfile[order].data;
    let cellData = targetSheetData[row][column];
    let return_v;

    if (getObjType(cellData) == "object") {
        return_v = cellData[type];

        if (type == "f" && return_v != null) {
            return_v = formula.functionHTMLGenerate(return_v);
        }
        else if (type == "f") {
            return_v = cellData["v"];
        }
        else if (cellData && cellData.ct) {
            if (cellData.ct.fa == 'yyyy-MM-dd') {
                return_v = cellData.m;
            }
            // 修复当单元格内有换行获取不到值的问题
            else if (cellData.ct.hasOwnProperty("t") && cellData.ct.t === 'inlineStr') {
                let inlineStrValueArr = cellData.ct.s;
                if (inlineStrValueArr) {
                    return_v = inlineStrValueArr.map(i => i.v).join("")
                }
            }
        }

    }

    if (return_v == undefined) {
        return_v = null;
    }

    return return_v;
}

/**
 * 设置单元格的值
 *
 * 关键点：如果设置了公式，则需要更新公式链insertUpdateFunctionGroup，如果设置了不是公式，判断之前是公式，则需要清除公式delFunctionGroup
 *
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object | String | Number} value 要设置的值；可以为字符串或数字，或为符合Luckysheet单元格格式的对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为`true`
 * @param {Function} options.success 操作结束的回调函数
 */
export function setCellValue(row, column, value, options = {}) {

    let curv = Store.flowdata[row][column];

    // Store old value for hook function
    const oldValue = JSON.stringify(curv);

    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('The row or column parameter is invalid.', '');
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        isRefresh = true,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    /* cell更新前触发  */
    if (!method.createHookFunction("cellUpdateBefore", row, column, value, isRefresh)) {
        /* 如果cellUpdateBefore函数返回false 则不执行后续的更新 */
        return;
    }

    let data = file.data;
    if (isRefresh) {
        data = $.extend(true, [], file.data);
    }
    if (data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    // luckysheetformula.updatecell(row, column, value);
    let formatList = {
        //ct:1, //celltype,Cell value format: text, time, etc.
        bg: 1,//background,#fff000
        ff: 1,//fontfamily,
        fc: 1,//fontcolor
        bl: 1,//Bold
        it: 1,//italic
        fs: 1,//font size
        cl: 1,//Cancelline, 0 Regular, 1 Cancelline
        un: 1,//underline, 0 Regular, 1 underlines, fonts
        vt: 1,//Vertical alignment, 0 middle, 1 up, 2 down
        ht: 1,//Horizontal alignment,0 center, 1 left, 2 right
        mc: 1, //Merge Cells
        tr: 1, //Text rotation,0: 0、1: 45 、2: -45、3 Vertical text、4: 90 、5: -90
        tb: 1, //Text wrap,0 truncation, 1 overflow, 2 word wrap
        //v: 1, //Original value
        //m: 1, //Display value
        rt: 1, //text rotation angle 0-180 alignment
        //f: 1, //formula
        qp: 1 //quotePrefix, show number as string
    }

    if (value == null || value.toString().length == 0) {
        formula.delFunctionGroup(row, column);
        setcellvalue(row, column, data, value);
    }
    else if (value instanceof Object) {
        let curv = {};
        if (isRealNull(data[row][column])) {
            data[row][column] = {};
        }
        let cell = data[row][column];
        if (value.f != null && value.v == null) {
            curv.f = value.f;
            if (value.ct != null) {
                curv.ct = value.ct;
            }
            data = luckysheetformula.updatecell(row, column, curv, false).data;//update formula value
        }
        else {
            if (value.ct != null) {
                curv.ct = value.ct;
            }
            if (value.f != null) {
                curv.f = value.f;
            }
            if (value.v != null) {
                curv.v = value.v;
            }
            else {
                curv.v = cell.v;
            }
            if (value.m != null) {
                curv.m = value.m;
            }
            formula.delFunctionGroup(row, column);
            setcellvalue(row, column, data, curv);//update text value
        }
        for (let attr in value) {
            let v = value[attr];
            if (attr in formatList) {
                menuButton.updateFormatCell(data, attr, v, row, row, column, column);//change range format
            }
            else {
                cell[attr] = v;
            }
        }
        data[row][column] = cell;
    }
    else {
        if (value.toString().substr(0, 1) == "=" || value.toString().substr(0, 5) == "<span") {
            data = luckysheetformula.updatecell(row, column, value, false).data;//update formula value or convert inline string html to object
        }
        else {
            formula.delFunctionGroup(row, column);
            setcellvalue(row, column, data, value);
        }
    }

    /* cell更新后触发  */
    setTimeout(() => {
        // Hook function
        method.createHookFunction("cellUpdated", row, column, JSON.parse(oldValue), Store.flowdata[row][column], isRefresh);
    }, 0);

    if (file.index == Store.currentSheetIndex && isRefresh) {
        jfrefreshgrid(data, [{ "row": [row, row], "column": [column, column] }]);//update data, meanwhile refresh canvas and store data to history
    }
    else {
        file.data = data;//only update data
    }

    if (success && typeof success === 'function') {
        success(data);
    }
}

/**
 * 清除指定工作表指定单元格的内容，返回清除掉的数据，不同于删除单元格的功能，不需要设定单元格移动情况
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearCell(row, column, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('Arguments row and column cannot be null or undefined.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options }

    let targetSheetData = $.extend(true, [], Store.luckysheetfile[order].data);
    let cell = targetSheetData[row][column];

    if (getObjType(cell) == "object") {
        delete cell["m"];
        delete cell["v"];

        if (cell["f"] != null) {
            delete cell["f"];
            formula.delFunctionGroup(row, column, order);

            delete cell["spl"];
        }
    }
    else {
        cell = null;
    }

    // 若操作为当前sheet页，则刷新当前sheet页
    if (order === curSheetOrder) {
        jfrefreshgrid(targetSheetData, [{
            row: [row, row],
            column: [column, column]
        }])
    }
    else {
        Store.luckysheetfile[order].data = targetSheetData;
    }

    if (success && typeof success === 'function') {
        success(cell)
    }
}

/**
 * 删除指定工作表指定单元格，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，右侧还是下方的单元格移动。可选值为 'left'、'up'
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteCell(move, row, column, options = {}) {
    let moveTypes = ['left', 'up'];
    if (!move || moveTypes.indexOf(move) < 0) {
        return tooltip.info('Arguments move cannot be null or undefined and its value must be \'left\' or \'up\'', '')
    }

    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('Arguments row and column cannot be null or undefined.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options }

    let moveType = 'move' + move.replace(move[0], move[0].toUpperCase()); // left-moveLeft;  up-moveUp

    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetDeleteCell(moveType, row, row, column, column, sheetIndex);

    if (success && typeof success === 'function') {
        success()
    }
}

/**
 * 设置某个单元格的属性，如果要设置单元格的值或者同时设置多个单元格属性，推荐使用setCellValue
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {String} attr
 * @param {Number | String | Object} value 具体的设置值，一个属性会对应多个值，参考 单元格属性表的值示例，特殊情况：如果属性类型attr是单元格格式ct，则设置值value应提供ct.fa，比如设置A1单元格的格式为百分比格式：luckysheet.setCellFormat(0, 0, "ct", "0.00%")
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为改变后的cell对象
 */
export function setCellFormat(row, column, attr, value, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('Arguments row or column cannot be null or undefined.', '')
    }

    if (!attr) {
        return tooltip.info('Arguments attr cannot be null or undefined.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let targetSheetData = $.extend(true, [], file.data);
    if (targetSheetData.length == 0) {
        targetSheetData = sheetmanage.buildGridData(file);
    }

    let cellData = targetSheetData[row][column] || {};
    let cfg = $.extend(true, {}, file.config);

    // 特殊格式
    if (attr == 'ct' && (!value || !value.hasOwnProperty('fa') || !value.hasOwnProperty('t'))) {
        return new TypeError('While set attribute \'ct\' to cell, the value must have property \'fa\' and \'t\'')
    }

    if (attr == 'bd') {
        if (cfg["borderInfo"] == null) {
            cfg["borderInfo"] = [];
        }

        let borderInfo = {
            rangeType: "range",
            borderType: "border-all",
            color: "#000",
            style: "1",
            range: [{
                column: [column, column],
                row: [row, row]
            }],
            ...value,
        }

        cfg["borderInfo"].push(borderInfo);
    } else {
        cellData[attr] = value;
    }

    targetSheetData[row][column] = cellData;

    // refresh
    if (file.index == Store.currentSheetIndex) {
        file.config = cfg;
        Store.config = cfg;
        jfrefreshgrid(targetSheetData, [{ "row": [row, row], "column": [column, column] }]);
    }
    else {
        file.config = cfg;
        file.data = targetSheetData;
    }

    if (success && typeof success === 'function') {
        success(cellData);
    }
}

/**
 * 查找一个工作表中的指定内容，返回查找到的内容组成的单元格一位数组，数据格式同celldata
 * @param {String} content 要查找的内容 可以为正则表达式（不包含前后'/')
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false. 注意：正则中的规则需要转义，如\S需要写成 \\S
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {String} options.type 单元格属性；默认值为m
 */
export function find(content, options = {}) {
    if (!content && content != 0) {
        return tooltip.info('Search content cannot be null or empty', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        isRegularExpression = false,
        isWholeWord = false,
        isCaseSensitive = false,
        order = curSheetOrder,
        type = "m"
    } = { ...options };
    let targetSheetData = Store.luckysheetfile[order].data;

    let result = [];
    for (let i = 0; i < targetSheetData.length; i++) {
        const rowArr = targetSheetData[i];

        for (let j = 0; j < rowArr.length; j++) {
            const cell = rowArr[j];

            if (!cell) {
                continue;
            }

            // 添加cell的row, column属性
            // replace方法中的setCellValue中需要使用该属性
            cell.row = i;
            cell.column = j;

            if (isWholeWord) {
                if (isCaseSensitive) {
                    if (content.toString() == cell[type]) {
                        result.push(cell)
                    }
                } else {
                    if (cell[type] && content.toString().toLowerCase() == cell[type].toLowerCase()) {
                        result.push(cell)
                    }
                }
            } else if (isRegularExpression) {
                let reg;
                if (isCaseSensitive) {
                    reg = new RegExp(func_methods.getRegExpStr(content), 'g')
                } else {
                    reg = new RegExp(func_methods.getRegExpStr(content), 'ig')
                }
                if (reg.test(cell[type])) {
                    result.push(cell)
                }
            } else if (isCaseSensitive) {
                let reg = new RegExp(func_methods.getRegExpStr(content), 'g');
                if (reg.test(cell[type])) {
                    result.push(cell);
                }
            } else {
                let reg = new RegExp(func_methods.getRegExpStr(content), 'ig');
                if (reg.test(cell[type])) {
                    result.push(cell);
                }
            }
        }
    }

    return result;
}

/**
 * 查找一个工作表中的指定内容并替换成新的内容，返回替换后的内容组成的单元格一位数组，数据格式同celldata。
 * @param {String} content 要查找的内容
 * @param {String} replaceContent 要替换的内容
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为替换后的cell集合
 */
export function replace(content, replaceContent, options = {}) {
    let matchCells = find(content, options)
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }
    let sheetData = $.extend(true, [], file.data);

    matchCells.forEach(cell => {
        cell.m = replaceContent;
        setCellValue(cell.row, cell.column, replaceContent, { order: order, isRefresh: false });
    })

    let fileData = $.extend(true, [], file.data);
    file.data.length = 0;
    file.data.push(...sheetData);

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, undefined, undefined, true, false);
    }

    luckysheetrefreshgrid();

    if (options.success && typeof options.success === 'function') {
        options.success(matchCells)
    }
    return matchCells;
}


/**
 * 手动触发退出编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function exitEditMode(options = {}) {
    if (parseInt($("#luckysheet-input-box").css("top")) > 0) {


        if ($("#luckysheet-formula-search-c").is(":visible") && formula.searchFunctionCell != null) {
            formula.searchFunctionEnter($("#luckysheet-formula-search-c").find(".luckysheet-formula-search-item-active"));
        }
        else {
            formula.updatecell(Store.luckysheetCellUpdate[0], Store.luckysheetCellUpdate[1]);
            Store.luckysheet_select_save = [{
                "row": [Store.luckysheetCellUpdate[0], Store.luckysheetCellUpdate[0]],
                "column": [Store.luckysheetCellUpdate[1], Store.luckysheetCellUpdate[1]],
                "row_focus": Store.luckysheetCellUpdate[0],
                "column_focus": Store.luckysheetCellUpdate[1]
            }];
        }

        //若有参数弹出框，隐藏
        if ($("#luckysheet-search-formula-parm").is(":visible")) {
            $("#luckysheet-search-formula-parm").hide();
        }
        //若有参数选取范围弹出框，隐藏
        if ($("#luckysheet-search-formula-parm-select").is(":visible")) {
            $("#luckysheet-search-formula-parm-select").hide();
        }

    }

    if (options.success && typeof options.success === 'function') {
        options.success();
    }
}

/**
 * 手动触发进入编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function enterEditMode(options = {}) {

    if ($("#luckysheet-conditionformat-dialog").is(":visible")) {
        return;
    }
    else if ($("#luckysheet-cell-selected").is(":visible")) {
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        let row_index = last["row_focus"], col_index = last["column_focus"];

        luckysheetupdateCell(row_index, col_index, Store.flowdata);
    }

    if (options.success && typeof options.success === 'function') {
        options.success();
    }
}


/**
 * 冻结首行
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstRow(order) {
    // store frozen
    luckysheetFreezen.saveFrozen("freezenRow", order);

    // 冻结为当前sheet页
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let freezenhorizontaldata, row_st, top;
        if (luckysheetFreezen.freezenRealFirstRowColumn) {
            let row_st = 0;
            top = Store.visibledatarow[row_st] - 2 + Store.columnHeaderHeight;
            freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                0,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
        } else {
            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);
            if (row_st == -1) {
                row_st = 0;
            }

            top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
        }

        luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结首列
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstColumn(order) {
    // store frozen
    luckysheetFreezen.saveFrozen("freezenColumn", order);

    // 冻结为当前sheet页
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let freezenverticaldata, col_st, left;
        if (luckysheetFreezen.freezenRealFirstRowColumn) {
            col_st = 0;
            left = Store.visibledatacolumn[col_st] - 2 + Store.rowHeaderWidth;
            freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                0,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
        } else {
            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();

            col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);
            if (col_st == -1) {
                col_st = 0;
            }

            left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
        }

        luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结行选区
 * @param {Object} range 行选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenRowRange(range, order) {
    const locale_frozen = locale().freezen;

    if (!range || (!range.hasOwnProperty('row_focus') && !formula.iscelldata(range))) {
        if (isEditMode()) {
            alert(locale_frozen.noSeletionError);
        } else {
            tooltip.info(locale_frozen.noSeletionError, "");
        }
        return
    }

    if (typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
        range = {
            row_focus: range.row[0],
            column_focus: range.column[0]
        }
    }
    // store frozen
    luckysheetFreezen.saveFrozen("freezenRowRange", order, range);

    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let scrollTop = $("#luckysheet-cell-main").scrollTop();
        let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);

        let row_focus = range.row_focus;
        if (row_focus > row_st) {
            row_st = row_focus;
        }
        if (row_st == -1) {
            row_st = 0;
        }

        let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
        let freezenhorizontaldata = [
            Store.visibledatarow[row_st],
            row_st + 1,
            scrollTop,
            luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
            top
        ];
        luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }

}

/**
 * 冻结列选区
 * @param {Object} range 列选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenColumnRange(range, order) {
    const locale_frozen = locale().freezen;
    let isStringRange = typeof range === 'string' && formula.iscelldata(range);

    if (!range || (!range.hasOwnProperty('column_focus') && !isStringRange)) {
        if (isEditMode()) {
            alert(locale_frozen.noSeletionError);
        } else {
            tooltip.info(locale_frozen.noSeletionError, "");
        }
        return
    }

    if (isStringRange) {
        range = formula.getcellrange(range)
        range = {
            row_focus: range.row[0],
            column_focus: range.column[0]
        }
    }
    // store frozen
    luckysheetFreezen.saveFrozen("freezenColumnRange", order, range);

    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
        let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);

        let column_focus = range.column_focus;
        if (column_focus > col_st) {
            col_st = column_focus;
        }
        if (col_st == -1) {
            col_st = 0;
        }

        let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
        let freezenverticaldata = [
            Store.visibledatacolumn[col_st],
            col_st + 1,
            scrollLeft,
            luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
            left
        ];
        luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 取消冻结
 * @param {Number | String} order
 */
export function cancelFrozen(order) {
    luckysheetFreezen.saveFrozen("freezenCancel", order);

    // 取消当前sheet冻结时，刷新canvas
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
        }
        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
        }
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结行操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行到选区 true-冻结行到选区  false-冻结首行
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setHorizontalFrozen(isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    if (!isRange) {
        frozenFirstRow(order)
    } else { // 选区行冻结
        frozenRowRange(range, order);
    }

    if (success && typeof success === 'function') {
        success()
    }
}

/**
 * 冻结列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结列到选区 true-冻结列到选区  false-冻结首列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setVerticalFrozen(isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    if (!isRange) {
        frozenFirstColumn(order);
    } else {
        frozenColumnRange(range, order);
    }

    if (success && typeof success === 'function') {
        success()
    }
}

/**
 * 冻结行列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行列到选区 true-冻结行列到选区  false-冻结首行列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setBothFrozen(isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    let isCurrentSheet = !order || order == getSheetIndex(Store.currentSheetIndex);
    const locale_frozen = locale().freezen;

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    // 冻结首行列
    if (!isRange) {
        // store frozen
        luckysheetFreezen.saveFrozen("freezenRC", order)

        if (isCurrentSheet) {
            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);
            if (row_st == -1) {
                row_st = 0;
            }
            let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            let freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
            luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

            luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);

            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
            let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);
            if (col_st == -1) {
                col_st = 0;
            }
            let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            let freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
            luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

            luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }
    } else {   // 冻结行列到选区
        // store frozen
        luckysheetFreezen.saveFrozen("freezenRCRange", order, range)

        let isStringRange = typeof range === 'string' && formula.iscelldata(range);
        if (isCurrentSheet) {
            if ((!range || !(range.hasOwnProperty('column_focus') && range.hasOwnProperty('row_focus'))) && !isStringRange) {
                if (isEditMode()) {
                    alert(locale_frozen.noSeletionError);
                } else {
                    tooltip.info(locale_frozen.noSeletionError, "");
                }
                return
            }

            if (isStringRange) {
                range = formula.getcellrange(range)
                range = {
                    row_focus: range.row[0],
                    column_focus: range.column[0]
                }
            }

            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);

            let row_focus = range.row_focus;

            if (row_focus > row_st) {
                row_st = row_focus;
            }

            if (row_st == -1) {
                row_st = 0;
            }

            let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            let freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
            luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

            luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);

            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
            let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);

            let column_focus = range.column_focus;

            if (column_focus > col_st) {
                col_st = column_focus;
            }

            if (col_st == -1) {
                col_st = 0;
            }

            let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            let freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
            luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

            luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }
    }
}

/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowOrColumn(type, index = 0, options = {}) {
    if (!isRealNum(index)) {
        return tooltip.info('The index parameter is invalid.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        number = 1,
        order = curSheetOrder,
        success
    } = { ...options }

    let _locale = locale();
    let locale_info = _locale.info;
    if (!isRealNum(number)) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumber);
        } else {
            tooltip.info(locale_info.tipInputNumber, "");
        }
        return;
    }

    number = parseInt(number);
    if (number < 1 || number > 100) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumberLimit);
        } else {
            tooltip.info(locale_info.tipInputNumberLimit, "");
        }
        return;
    }

    // 默认在行上方增加行，列左侧增加列
    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetextendtable(type, index, number, "lefttop", sheetIndex);

    if (success && typeof success === 'function') {
        success();
    }
}
/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottomOrColumnRight(type, index = 0, options = {}) {
    if (!isRealNum(index)) {
        return tooltip.info('The index parameter is invalid.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        number = 1,
        order = curSheetOrder,
        success
    } = { ...options }

    let _locale = locale();
    let locale_info = _locale.info;
    if (!isRealNum(number)) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumber);
        } else {
            tooltip.info(locale_info.tipInputNumber, "");
        }
        return;
    }

    number = parseInt(number);
    if (number < 1 || number > 100) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumberLimit);
        } else {
            tooltip.info(locale_info.tipInputNumberLimit, "");
        }
        return;
    }

    // 默认在行上方增加行，列左侧增加列
    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetextendtable(type, index, number, "rightbottom", sheetIndex);

    if (success && typeof success === 'function') {
        success();
    }
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRow(row = 0, options = {}) {
    insertRowOrColumn('row', row, options)
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottom(row = 0, options = {}) {
    insertRowBottomOrColumnRight('row', row, options)
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumn(column = 0, options = {}) {
    insertRowOrColumn('column', column, options)
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumnRight(column = 0, options = {}) {
    insertRowBottomOrColumnRight('column', column, options)
}
/**
 * 删除指定的行或列。删除行列之后，行列的序号并不会变化，下面的行（右侧的列）会补充到上（左）面，注意观察数据是否被正确删除即可。
 * @param {String} type 删除行或列 row-行  column-列
 * @param {Number} startIndex 要删除的起始行或列
 * @param {Number} endIndex 要删除的结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRowOrColumn(type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options }


    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }
    luckysheetdeletetable(type, startIndex, endIndex, sheetIndex);

    if (success && typeof success === 'function') {
        success()
    }
}

/**
 * 删除指定的行。
 * @param {Number} rowStart 要删除的起始行
 * @param {Number} rowEnd 要删除的结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRow(rowStart, rowEnd, options = {}) {
    deleteRowOrColumn('row', rowStart, rowEnd, options)
}

/**
 * 删除指定的列。
 * @param {Number} columnStart 要删除的起始列
 * @param {Number} columnEnd 要删除的结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteColumn(columnStart, columnEnd, options = {}) {
    deleteRowOrColumn('column', columnStart, columnEnd, options)
}

/**
 * 隐藏行或列
 * @param {String} type 隐藏行或列  row-隐藏行  column-隐藏列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideRowOrColumn(type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = $.extend(true, {}, file.config);
    if (cfg[cfgKey] == null) {
        cfg[cfgKey] = {};
    }

    for (let i = startIndex; i <= endIndex; i++) {
        cfg[cfgKey][i] = 0;
    }

    //保存撤销
    if (Store.clearjfundo) {
        let redo = {};
        redo["type"] = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo["sheetIndex"] = file.index;
        redo["config"] = $.extend(true, {}, file.config);
        redo["curconfig"] = cfg;

        Store.jfundo.length = 0;
        Store.jfredo.push(redo);
    }

    Store.luckysheetfile[order].config = cfg;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }

    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order == curSheetOrder) {
        //config
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 显示隐藏的行或列
 * @param {String} type 显示行或列  row-显示行  column-显示列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRowOrColumn(type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = $.extend(true, {}, file.config);
    if (cfg[cfgKey] == null) {
        return;
    }

    for (let i = startIndex; i <= endIndex; i++) {
        delete cfg[cfgKey][i];
    }

    //保存撤销
    if (Store.clearjfundo) {
        let redo = {};
        redo["type"] = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo["sheetIndex"] = file.index;
        redo["config"] = $.extend(true, {}, file.config);
        redo["curconfig"] = cfg;

        Store.jfundo.length = 0;
        Store.jfredo.push(redo);
    }

    //config
    Store.luckysheetfile[order].config = Store.config;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }

    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order === curSheetOrder) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 隐藏行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideRow(startIndex, endIndex, options = {}) {
    hideRowOrColumn('row', startIndex, endIndex, options);
}

/**
 * 显示行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRow(startIndex, endIndex, options = {}) {
    showRowOrColumn('row', startIndex, endIndex, options);
}

/**
 * 隐藏列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideColumn(startIndex, endIndex, options = {}) {
    hideRowOrColumn('column', startIndex, endIndex, options);
}

/**
 * 显示列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showColumn(startIndex, endIndex, options = {}) {
    showRowOrColumn('column', startIndex, endIndex, options);
}


/**
 * 设置指定行的高度。优先级最高，高于默认行高和用户自定义行高。
 * @param {Object} rowInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRowHeight(rowInfo, options = {}) {
    if (getObjType(rowInfo) != 'object') {
        return tooltip.info("The rowInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    if (cfg['rowlen'] == null) {
        cfg['rowlen'] = {};
    }

    for (let r in rowInfo) {
        if (parseInt(r) >= 0) {
            let len = rowInfo[r];

            if (len === 'auto') {
                cfg['rowlen'][parseInt(r)] = len
            } else {
                if (Number(len) >= 0) {
                    cfg['rowlen'][parseInt(r)] = Number(len);
                }
            }
        }
    }

    file.config = cfg;

    server.saveParam("cg", file.index, cfg["rowlen"], { "k": "rowlen" });

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
    }

    if (success && typeof success === 'function') {
        success()
    }
}


/**
 * 设置指定列的宽度
 * @param {Object} columnInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setColumnWidth(columnInfo, options = {}) {
    if (getObjType(columnInfo) != 'object') {
        return tooltip.info("The columnInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    if (cfg['columnlen'] == null) {
        cfg['columnlen'] = {};
    }

    for (let c in columnInfo) {
        if (parseInt(c) >= 0) {
            let len = columnInfo[c];

            if (len === 'auto') {
                cfg['columnlen'][parseInt(c)] = len
            } else {
                if (Number(len) >= 0) {
                    cfg['columnlen'][parseInt(c)] = Number(len);
                }
            }
        }
    }

    file.config = cfg;

    server.saveParam("cg", file.index, cfg["columnlen"], { "k": "columnlen" });

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
    }

    if (success && typeof success === 'function') {
        success()
    }
}


/**
 * 获取指定工作表指定行的高度，得到行号和高度对应关系的对象
 * @param {Array} rowInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getRowHeight(rowInfo, options = {}) {
    if (getObjType(rowInfo) != 'array' || rowInfo.length == 0) {
        return tooltip.info("The rowInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let rowlen = cfg["rowlen"] || {};

    let rowlenObj = {};

    rowInfo.forEach((item) => {
        if (parseInt(item) >= 0) {
            let size = rowlen[parseInt(item)] || Store.defaultrowlen;
            rowlenObj[parseInt(item)] = size;
        }
    })

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success()
        }
    }, 1)

    return rowlenObj;
}


/**
 * 获取指定工作表指定列的宽度，得到列号和宽度对应关系的对象
 * @param {Array} columnInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getColumnWidth(columnInfo, options = {}) {
    if (getObjType(columnInfo) != 'array' || columnInfo.length == 0) {
        return tooltip.info("The columnInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let columnlen = cfg["columnlen"] || {};

    let columnlenObj = {};

    columnInfo.forEach((item) => {
        if (parseInt(item) >= 0) {
            let size = columnlen[parseInt(item)] || Store.defaultcollen;
            columnlenObj[parseInt(item)] = size;
        }
    })

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success()
        }
    }, 1)

    return columnlenObj;
}


/**
 * 获取工作表的默认行高
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultRowHeight(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success()
        }
    }, 1)

    // *返回指定的工作表默认行高，如果未配置就返回全局的默认行高
    return Store.luckysheetfile[order].defaultRowHeight || Store.defaultrowlen;
}


/**
 * 获取工作表的默认列宽
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultColWidth(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success()
        }
    }, 1)

    // *返回指定的工作表默认列宽，如果未配置就返回全局的默认列宽
    return Store.luckysheetfile[order].defaultColWidth || Store.defaultcollen;
}


/**
 * 返回当前选区对象的数组，可能存在多个选区。
 * 每个选区的格式为row/column信息组成的对象{row:[0,1],column:[0,1]}
 * @returns {Array}
 */
export function getRange() {
    let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));

    let result = [];

    for (let i = 0; i < rangeArr.length; i++) {
        let rangeItem = rangeArr[i];
        let temp = {
            row: rangeItem.row,
            column: rangeItem.column
        }
        result.push(temp)
    }

    return result;
}

/**
 * 返回表示指定区域内所有单元格位置的数组，区别getRange方法，该方法以cell单元格(而非某块连续的区域)为单位来组织选区的数据
 * @param   {Array}   range 可选参数，默认为当前选中区域
 * @returns {Array}   对象数组
 */
export function getRangeWithFlatten(range) {
    range = range || getRange();

    let result = [];

    range.forEach(ele => {
        // 这个data可能是个范围或者是单个cell
        let rs = ele.row;
        let cs = ele.column;
        for (let r = rs[0]; r <= rs[1]; r++) {
            for (let c = cs[0]; c <= cs[1]; c++) {
                // r c 当前的r和当前的c
                result.push({ r, c });
            }
        }
    })
    return result;
}

/**
 * 返回表示指定区域内所有单元格内容的对象数组
 * @param   {Array}   range 可选参数，默认为当前选中区域扁平化后的对象，结构形如[{r:0,c:0},{r:0,c:1}...]
 * @returns {Array}   对象数组
 */
export function getRangeValuesWithFlatte(range) {
    range = range || getRangeWithFlatten();

    let values = [];

    // 获取到的这个数据不是最新的数据
    range.forEach(item => {
        values.push(Store.flowdata[item.r][item.c]);
    });
    return values;
}


/**
 * 返回对应当前选区的坐标字符串数组，可能存在多个选区。
 * 每个选区可能是单个单元格(如 A1)或多个单元格组成的矩形区域(如 D9:E12)
 * @returns {Array}
 */
export function getRangeAxis() {
    let result = [];
    let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let sheetIndex = Store.currentSheetIndex;

    rangeArr.forEach(ele => {
        let axisText = getRangetxt(sheetIndex, { column: ele.column, row: ele.row });
        result.push(axisText);
    })

    return result;
}

/**
 * 返回指定工作表指定范围的单元格二维数组数据，每个单元格为一个对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeValue(options = {}) {
    let curOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curOrder
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (!range || typeof range === 'object') {
        return getdatabyselection(range, file.index);
    } else if (typeof range === 'string') {
        if (formula.iscelldata(range)) {
            return getdatabyselection(formula.getcellrange(range), file.index)
        } else {
            tooltip.info('The range is invalid, please check range parameter.', '')
        }
    }
}


/**
 * 复制指定工作表指定单元格区域的数据，返回包含`<table>`html格式的数据，可用于粘贴到excel中保持单元格样式。
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {order} options.order 工作表下标
 */
export function getRangeHtml(options = {}) {
    let {
        range = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }
    range = JSON.parse(JSON.stringify(range));

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    //复制范围内包含部分合并单元格，提示
    let cfg = $.extend(true, {}, file.config);
    if (cfg["merge"] != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info("Cannot perform this operation on partially merged cells", "");
        }
    }

    //多重选区 有条件格式时 提示
    let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);
    if (range.length > 1 && cdformat.length > 0) {
        let hasCF = false;
        let cf_compute = conditionformat.getComputeMap(file.index);

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            for (let r = r1; r <= r2; r++) {
                for (let c = c1; c <= c2; c++) {
                    if (conditionformat.checksCF(r, c, cf_compute) != null) {
                        hasCF = true;
                        break;
                    }
                }

                if (hasCF) {
                    break;
                }
            }

            if (hasCF) {
                break;
            }
        }

        if (hasCF) {
            return tooltip.info("Cannot perform this operation on multiple selection areas, please select a single area", "");
        }
    }

    //多重选区 行不一样且列不一样时 提示
    if (range.length > 1) {
        let isSameRow = true,
            str_r = range[0].row[0],
            end_r = range[0].row[1];
        let isSameCol = true,
            str_c = range[0].column[0],
            end_c = range[0].column[1];

        for (let s = 1; s < range.length; s++) {
            if (range[s].row[0] != str_r || range[s].row[1] != end_r) {
                isSameRow = false;
            }

            if (range[s].column[0] != str_c || range[s].column[1] != end_c) {
                isSameCol = false;
            }
        }

        if ((!isSameRow && !isSameCol) || selectIsOverlap(range)) {
            return tooltip.info("Cannot perform this operation on multiple selection areas, please select a single area", "");
        }
    }

    let rowIndexArr = [], colIndexArr = [];

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        for (let r = r1; r <= r2; r++) {
            if (cfg["rowhidden"] != null && cfg["rowhidden"][r] != null) {
                continue;
            }

            if (!rowIndexArr.includes(r)) {
                rowIndexArr.push(r);
            }

            for (let c = c1; c <= c2; c++) {
                if (cfg["colhidden"] != null && cfg["colhidden"][c] != null) {
                    continue;
                }

                if (!colIndexArr.includes(c)) {
                    colIndexArr.push(c);
                }
            }
        }
    }

    let borderInfoCompute;
    if (cfg["borderInfo"] && cfg["borderInfo"].length > 0) { //边框
        borderInfoCompute = getBorderInfoCompute(file.index);
    }

    let d = file.data;
    if (d == null || d.length == 0) {
        d = sheetmanage.buildGridData(file);
    }

    let cpdata = "";
    let colgroup = "";

    rowIndexArr = rowIndexArr.sort((a, b) => a - b);
    colIndexArr = colIndexArr.sort((a, b) => a - b);

    for (let i = 0; i < rowIndexArr.length; i++) {
        let r = rowIndexArr[i];

        if (cfg["rowhidden"] != null && cfg["rowhidden"][r] != null) {
            continue;
        }

        cpdata += '<tr>';

        for (let j = 0; j < colIndexArr.length; j++) {
            let c = colIndexArr[j];

            if (cfg["colhidden"] != null && cfg["colhidden"][c] != null) {
                continue;
            }

            let column = '<td ${span} style="${style}">';

            if (d[r] != null && d[r][c] != null) {
                let style = "", span = "";

                if (r == rowIndexArr[0]) {
                    if (cfg["columnlen"] == null || cfg["columnlen"][c.toString()] == null) {
                        colgroup += '<colgroup width="72px"></colgroup>';
                    }
                    else {
                        colgroup += '<colgroup width="' + cfg["columnlen"][c.toString()] + 'px"></colgroup>';
                    }
                }

                if (c == colIndexArr[0]) {
                    if (cfg["rowlen"] == null || cfg["rowlen"][r.toString()] == null) {
                        style += 'height:19px;';
                    }
                    else {
                        style += 'height:' + cfg["rowlen"][r.toString()] + 'px;';
                    }
                }

                let reg = /^(w|W)((0?)|(0\.0+))$/;
                let c_value;
                if (d[r][c].ct != null && d[r][c].ct.fa != null && d[r][c].ct.fa.match(reg)) {
                    c_value = getcellvalue(r, c, d);
                }
                else {
                    c_value = getcellvalue(r, c, d, "m");
                }

                style += menuButton.getStyleByCell(d, r, c);

                if (getObjType(d[r][c]) == "object" && ("mc" in d[r][c])) {
                    if ("rs" in d[r][c]["mc"]) {
                        span = 'rowspan="' + d[r][c]["mc"].rs + '" colspan="' + d[r][c]["mc"].cs + '"';

                        //边框
                        if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                            let bl_obj = { "color": {}, "style": {} },
                                br_obj = { "color": {}, "style": {} },
                                bt_obj = { "color": {}, "style": {} },
                                bb_obj = { "color": {}, "style": {} };

                            for (let bd_r = r; bd_r < (r + d[r][c]["mc"].rs); bd_r++) {
                                for (let bd_c = c; bd_c < (c + d[r][c]["mc"].cs); bd_c++) {
                                    if (bd_r == r && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].t) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].t.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].t.color;

                                        if (bt_obj["style"][linetype] == null) {
                                            bt_obj["style"][linetype] = 1;
                                        }
                                        else {
                                            bt_obj["style"][linetype] = bt_obj["style"][linetype] + 1;
                                        }

                                        if (bt_obj["color"][bcolor] == null) {
                                            bt_obj["color"][bcolor] = 1;
                                        }
                                        else {
                                            bt_obj["color"][bcolor] = bt_obj["color"][bcolor] + 1;
                                        }
                                    }

                                    if (bd_r == (r + d[r][c]["mc"].rs - 1) && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].b) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].b.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].b.color;

                                        if (bb_obj["style"][linetype] == null) {
                                            bb_obj["style"][linetype] = 1;
                                        }
                                        else {
                                            bb_obj["style"][linetype] = bb_obj["style"][linetype] + 1;
                                        }

                                        if (bb_obj["color"][bcolor] == null) {
                                            bb_obj["color"][bcolor] = 1;
                                        }
                                        else {
                                            bb_obj["color"][bcolor] = bb_obj["color"][bcolor] + 1;
                                        }
                                    }

                                    if (bd_c == c && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].l) {
                                        let linetype = borderInfoCompute[r + "_" + c].l.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].l.color;

                                        if (bl_obj["style"][linetype] == null) {
                                            bl_obj["style"][linetype] = 1;
                                        }
                                        else {
                                            bl_obj["style"][linetype] = bl_obj["style"][linetype] + 1;
                                        }

                                        if (bl_obj["color"][bcolor] == null) {
                                            bl_obj["color"][bcolor] = 1;
                                        }
                                        else {
                                            bl_obj["color"][bcolor] = bl_obj["color"][bcolor] + 1;
                                        }
                                    }

                                    if (bd_c == (c + d[r][c]["mc"].cs - 1) && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].r) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].r.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].r.color;

                                        if (br_obj["style"][linetype] == null) {
                                            br_obj["style"][linetype] = 1;
                                        }
                                        else {
                                            br_obj["style"][linetype] = br_obj["style"][linetype] + 1;
                                        }

                                        if (br_obj["color"][bcolor] == null) {
                                            br_obj["color"][bcolor] = 1;
                                        }
                                        else {
                                            br_obj["color"][bcolor] = br_obj["color"][bcolor] + 1;
                                        }
                                    }
                                }
                            }

                            let rowlen = d[r][c]["mc"].rs, collen = d[r][c]["mc"].cs;

                            if (JSON.stringify(bl_obj).length > 23) {
                                let bl_color = null, bl_style = null;

                                for (let x in bl_obj.color) {
                                    if (bl_obj.color[x] >= (rowlen / 2)) {
                                        bl_color = x;
                                    }
                                }

                                for (let x in bl_obj.style) {
                                    if (bl_obj.style[x] >= (rowlen / 2)) {
                                        bl_style = x;
                                    }
                                }

                                if (bl_color != null && bl_style != null) {
                                    style += "border-left:" + selection.getHtmlBorderStyle(bl_style, bl_color);
                                }
                            }

                            if (JSON.stringify(br_obj).length > 23) {
                                let br_color = null, br_style = null;

                                for (let x in br_obj.color) {
                                    if (br_obj.color[x] >= (rowlen / 2)) {
                                        br_color = x;
                                    }
                                }

                                for (let x in br_obj.style) {
                                    if (br_obj.style[x] >= (rowlen / 2)) {
                                        br_style = x;
                                    }
                                }

                                if (br_color != null && br_style != null) {
                                    style += "border-right:" + selection.getHtmlBorderStyle(br_style, br_color);
                                }
                            }

                            if (JSON.stringify(bt_obj).length > 23) {
                                let bt_color = null, bt_style = null;

                                for (let x in bt_obj.color) {
                                    if (bt_obj.color[x] >= (collen / 2)) {
                                        bt_color = x;
                                    }
                                }

                                for (let x in bt_obj.style) {
                                    if (bt_obj.style[x] >= (collen / 2)) {
                                        bt_style = x;
                                    }
                                }

                                if (bt_color != null && bt_style != null) {
                                    style += "border-top:" + selection.getHtmlBorderStyle(bt_style, bt_color);
                                }
                            }

                            if (JSON.stringify(bb_obj).length > 23) {
                                let bb_color = null, bb_style = null;

                                for (let x in bb_obj.color) {
                                    if (bb_obj.color[x] >= (collen / 2)) {
                                        bb_color = x;
                                    }
                                }

                                for (let x in bb_obj.style) {
                                    if (bb_obj.style[x] >= (collen / 2)) {
                                        bb_style = x;
                                    }
                                }

                                if (bb_color != null && bb_style != null) {
                                    style += "border-bottom:" + selection.getHtmlBorderStyle(bb_style, bb_color);
                                }
                            }
                        }
                    }
                    else {
                        continue;
                    }
                }
                else {
                    //边框
                    if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                        //左边框
                        if (borderInfoCompute[r + "_" + c].l) {
                            let linetype = borderInfoCompute[r + "_" + c].l.style;
                            let bcolor = borderInfoCompute[r + "_" + c].l.color;
                            style += "border-left:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        //右边框
                        if (borderInfoCompute[r + "_" + c].r) {
                            let linetype = borderInfoCompute[r + "_" + c].r.style;
                            let bcolor = borderInfoCompute[r + "_" + c].r.color;
                            style += "border-right:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        //下边框
                        if (borderInfoCompute[r + "_" + c].b) {
                            let linetype = borderInfoCompute[r + "_" + c].b.style;
                            let bcolor = borderInfoCompute[r + "_" + c].b.color;
                            style += "border-bottom:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        //上边框
                        if (borderInfoCompute[r + "_" + c].t) {
                            let linetype = borderInfoCompute[r + "_" + c].t.style;
                            let bcolor = borderInfoCompute[r + "_" + c].t.color;
                            style += "border-top:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }
                    }
                }

                column = replaceHtml(column, { "style": style, "span": span });

                if (c_value == null) {
                    c_value = getcellvalue(r, c, d);
                }

                if (c_value == null) {
                    c_value = " ";
                }

                column += c_value;
            }
            else {
                let style = "";

                //边框
                if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                    //左边框
                    if (borderInfoCompute[r + "_" + c].l) {
                        let linetype = borderInfoCompute[r + "_" + c].l.style;
                        let bcolor = borderInfoCompute[r + "_" + c].l.color;
                        style += "border-left:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    //右边框
                    if (borderInfoCompute[r + "_" + c].r) {
                        let linetype = borderInfoCompute[r + "_" + c].r.style;
                        let bcolor = borderInfoCompute[r + "_" + c].r.color;
                        style += "border-right:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    //下边框
                    if (borderInfoCompute[r + "_" + c].b) {
                        let linetype = borderInfoCompute[r + "_" + c].b.style;
                        let bcolor = borderInfoCompute[r + "_" + c].b.color;
                        style += "border-bottom:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    //上边框
                    if (borderInfoCompute[r + "_" + c].t) {
                        let linetype = borderInfoCompute[r + "_" + c].t.style;
                        let bcolor = borderInfoCompute[r + "_" + c].t.color;
                        style += "border-top:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }
                }

                column += "";

                if (r == rowIndexArr[0]) {
                    if (cfg["columnlen"] == null || cfg["columnlen"][c.toString()] == null) {
                        colgroup += '<colgroup width="72px"></colgroup>';
                    }
                    else {
                        colgroup += '<colgroup width="' + cfg["columnlen"][c.toString()] + 'px"></colgroup>';
                    }
                }

                if (c == colIndexArr[0]) {
                    if (cfg["rowlen"] == null || cfg["rowlen"][r.toString()] == null) {
                        style += 'height:19px;';
                    }
                    else {
                        style += 'height:' + cfg["rowlen"][r.toString()] + 'px;';
                    }
                }

                column = replaceHtml(column, { "style": style, "span": "" });
                column += " ";
            }

            column += '</td>';
            cpdata += column;
        }

        cpdata += "</tr>";
    }

    cpdata = '<table data-type="luckysheet_copy_action_table">' + colgroup + cpdata + '</table>';

    return cpdata;
}


/**
 * 复制指定工作表指定单元格区域的数据，返回一维、二维或者自定义行列数的二维数组的数据。只有在dimensional设置为custom的时候，才需要设置setting中的row和column
 * @param {String} dimensional 数组维度。可选值为：oneDimensional-一维数组；twoDimensional-二维数组； custom-自定义行列数的二维数组
 * @param {Object} options 可选参数
 * @param {Number} options.row dimensional为custom的时候设置，多维数组的行数
 * @param {Number} options.column dimensional为custom的时候设置，多维数组的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeArray(dimensional, options = {}) {
    let dimensionalValues = ['oneDimensional', 'twoDimensional'];

    if (!dimensionalValues.includes(dimensional)) {
        return tooltip.info("The dimensional parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let r1 = range.row[0],
        r2 = range.row[1];
    let c1 = range.column[0],
        c2 = range.column[1];

    //复制范围内包含部分合并单元格，提示
    let cfg = $.extend(true, {}, file.config);
    if (cfg["merge"] != null) {
        let has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

        if (has_PartMC) {
            return tooltip.info("Cannot perform this operation on partially merged cells", "");
        }
    }

    let data = file.data;
    if (data == null || data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    let dataArr = [];

    if (dimensional == 'oneDimensional') {//一维数组
        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell == null || cell.v == null) {
                    dataArr.push(null);
                }
                else {
                    dataArr.push(cell.v);
                }
            }
        }
    }
    else if (dimensional == 'twoDimensional') {
        for (let r = r1; r <= r2; r++) {
            let row = [];

            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell == null || cell.v == null) {
                    row.push(null);
                }
                else {
                    row.push(cell.v);
                }
            }

            dataArr.push(row);
        }
    }

    return dataArr;
}

/**
 * 复制指定工作表指定单元格区域的数据，返回json格式的数据
 * @param {Boolean} isFirstRowTitle 是否首行为标题
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeJson(isFirstRowTitle, options = {}) {
    let curRange = Store.luckysheet_select_save[0];
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range = curRange,
        order = curSheetOrder
    } = { ...options }
    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale_drag.noMulti);
        } else {
            tooltip.info(locale_drag.noMulti, "");
        }
        return;
    }

    //复制范围内包含部分合并单元格，提示
    if (config["merge"] != null) {
        let has_PartMC = false;
        let r1 = range.row[0],
            r2 = range.row[1],
            c1 = range.column[0],
            c2 = range.column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, file.index);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }
    if (isFirstRowTitle) {
        if (getdata.length === 1) {
            let obj = {};
            for (let i = 0; i < getdata[0].length; i++) {
                obj[getcellvalue(0, i, getdata)] = "";
            }
            arr.push(obj);
        } else {
            for (let r = 1; r < getdata.length; r++) {
                let obj = {};
                for (let c = 0; c < getdata[0].length; c++) {
                    if (getcellvalue(0, c, getdata) == undefined) {
                        obj[""] = getcellvalue(r, c, getdata);
                    } else {
                        obj[getcellvalue(0, c, getdata)] = getcellvalue(r, c, getdata);
                    }
                }
                arr.push(obj);
            }
        }
    } else {
        let st = range["column"][0];
        for (let r = 0; r < getdata.length; r++) {
            let obj = {};
            for (let c = 0; c < getdata[0].length; c++) {
                obj[chatatABC(c + st)] = getcellvalue(r, c, getdata);
            }
            arr.push(obj);
        }
    }
    // selection.copybyformat(new Event('click'), JSON.stringify(arr));
    return arr;
}

/**
 *
 * @param {String} type 对角线还是对角线偏移 "normal"-对角线  "anti"-反对角线
"offset"-对角线偏移
 * @param {Object} options 可选参数
 * @param {Number} options.column type为offset的时候设置，对角偏移的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeDiagonal(type, options = {}) {
    let typeValues = ['normal', 'anti', 'offset'];
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'normal\', \'anti\', \'offset\']', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        column = 1,
        range = curRange,
        order = curSheetOrder
    } = { ...options }

    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale().drag.noMulti);
        } else {
            tooltip.info(locale().drag.noMulti, "");
        }
        return;
    }

    //复制范围内包含部分合并单元格，提示
    if (config["merge"] != null) {
        let has_PartMC = false;
        let r1 = range[0].row[0],
            r2 = range[0].row[1],
            c1 = range[0].column[0],
            c2 = range[0].column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, order);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }

    let clen = getdata[0].length;
    switch (type) {
        case 'normal':
            for (let r = 0; r < getdata.length; r++) {
                if (r >= clen) {
                    break;
                }
                arr.push(getdata[r][r]);
            }
            break;
        case 'anti':
            for (let r = 0; r < getdata.length; r++) {
                if (r >= clen) {
                    break;
                }
                arr.push(getdata[r][clen - r - 1]);
            }
            break;
        case 'offset':
            if (column.toString() == "NaN") {
                if (isEditMode()) {
                    alert(locale().drag.inputCorrect);
                } else {
                    tooltip.info(locale().drag.inputCorrect, "");
                }
                return;
            }

            if (column < 0) {
                if (isEditMode()) {
                    alert(locale().drag.offsetColumnLessZero);
                } else {
                    tooltip.info(locale().drag.offsetColumnLessZero, "");
                }
                return;
            }

            for (let r = 0; r < getdata.length; r++) {
                if (r + column >= clen) {
                    break;
                }
                arr.push(getdata[r][r + column]);
            }
            break;
    }
    selection.copybyformat(new Event(), JSON.stringify(arr));
}

/**
 * 复制指定工作表指定单元格区域的数据，返回布尔值的数据
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeBoolean(options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder
    } = { ...options }

    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale().drag.noMulti);
        } else {
            tooltip.info(locale().drag.noMulti, "");
        }
        return;
    }

    //复制范围内包含部分合并单元格，提示
    if (config["merge"] != null) {
        let has_PartMC = false;
        let r1 = range[0].row[0],
            r2 = range[0].row[1],
            c1 = range[0].column[0],
            c2 = range[0].column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, order);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }
    for (let r = 0; r < getdata.length; r++) {
        let a = [];
        for (let c = 0; c < getdata[0].length; c++) {
            let bool = false;

            let v;
            if (getObjType(getdata[r][c]) == "object") {
                v = getdata[r][c].v;
            } else {
                v = getdata[r][c];
            }

            if (v == null || v == "") {
                bool = false;
            } else {
                v = parseInt(v);
                if (v == null || v > 0) {
                    bool = true;
                } else {
                    bool = false;
                }
            }
            a.push(bool);
        }
        arr.push(a);
    }

    selection.copybyformat(event, JSON.stringify(arr));
}


/**
 * 指定工作表选中一个或多个选区为选中状态并选择是否高亮，支持多种格式设置。
 * @param {Array | Object | String} range 选区范围
 * @param {Object} options 可选参数
 * @param {Boolean} options.show 是否显示高亮选中效果；默认值为 `true`
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeShow(range, options = {}) {
    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) == 'array') {
        for (let i = 0; i < range.length; i++) {
            if (getObjType(range[i]) === 'string') {
                if (!formula.iscelldata(range[i])) {
                    return tooltip.info("The range parameter is invalid.", "");
                }
                let cellrange = formula.getcellrange(range[i]);
                range[i] = {
                    "row": cellrange.row,
                    "column": cellrange.column
                }
            }
            else if (getObjType(range) == 'object') {
                if (range.row == null || range.column == null) {
                    return tooltip.info("The range parameter is invalid.", "");
                }
                range = {
                    "row": range.row,
                    "column": range.column
                };
            }
        }
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let {
        show = true,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    for (let i = 0; i < range.length; i++) {
        let changeparam = menuButton.mergeMoveMain(range[i].column, range[i].row, range[i]);
        if (changeparam) {
            range[i] = {
                "row": changeparam[1],
                "column": changeparam[0]
            }
        }
    }

    file.luckysheet_select_save = range;

    if (file.index == Store.currentSheetIndex) {
        Store.luckysheet_select_save = range;
        selectHightlightShow();

        if (!show) {
            $("#luckysheet-cell-selected-boxs").hide();
            $("#luckysheet-cell-selected-focus").hide();
            $("#luckysheet-row-count-show").hide();
            $("#luckysheet-column-count-show").hide();
            $("#luckysheet-rows-h-selected").empty();
            $("#luckysheet-cols-h-selected").empty();
        }
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 将一个单元格数组数据赋值到指定的区域，数据格式同getRangeValue方法取到的数据。
 * @param {Array[Array]} data 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为true
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeValue(data, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
    let {
        range = curRange,
        isRefresh = true,
        order = curSheetOrder,
        success
    } = { ...options }

    if (data == null) {
        return tooltip.info('The data which will be set to range cannot be null.', '')
    }

    if (range instanceof Array) {
        return tooltip.info('setRangeValue only supports a single selection.', '')
    }

    if (typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    let rowCount = range.row[1] - range.row[0] + 1,
        columnCount = range.column[1] - range.column[0] + 1;

    if (data.length !== rowCount || data[0].length !== columnCount) {
        return tooltip.info('The data to be set does not match the selection.', '')
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }
    let sheetData = $.extend(true, [], file.data);

    for (let i = 0; i < rowCount; i++) {
        for (let j = 0; j < columnCount; j++) {
            let row = range.row[0] + i,
                column = range.column[0] + j;
            setCellValue(row, column, data[i][j], { order: order, isRefresh: false })
        }
    }

    let fileData = $.extend(true, [], file.data);
    file.data.length = 0;
    file.data.push(...sheetData);

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, [{
            row: range.row,
            column: range.column,
        }], undefined, true, false);
    }

    if (isRefresh) {
        luckysheetrefreshgrid();
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式，赋值操作推荐使用setRangeValue方法
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function setSingleRangeFormat(attr, value, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
    let {
        range = curRange,
        order = curSheetOrder,
    } = { ...options }

    if (!attr) {
        tooltip.info('Arguments attr cannot be null or undefined.', '')
        return 'error';
    }

    if (range instanceof Array) {
        tooltip.info('setRangeValue only supports a single selection.', '')
        return 'error';
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            tooltip.info("The range parameter is invalid.", "");
            return 'error';
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        tooltip.info("The range parameter is invalid.", "");
        return 'error';
    }

    for (let r = range.row[0]; r <= range.row[1]; r++) {
        for (let c = range.column[0]; c <= range.column[1]; c++) {
            console.log('r', r);
            console.log('c', c);
            setCellValue(r, c, { [attr]: value }, {
                order: order,
                isRefresh: false,
            })
        }
    }
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式。支持多选区设置
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeFormat(attr, value, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    let result = []

    for (let i = 0; i < range.length; i++) {
        result.push(setSingleRangeFormat(attr, value, { range: range[i], order: order }));
    }

    let fileData = $.extend(true, [], file.data);
    if (result.some(i => i === 'error')) {
        file.data.length = 0;
        file.data.push(...fileData);
        return false;
    }

    file.data.length = 0;
    file.data.push(...fileData);

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, undefined, undefined, true, false);
    }

    luckysheetrefreshgrid();

    if (success && typeof success === 'function') {
    }
}

/**
 * 为指定索引的工作表，选定的范围开启或关闭筛选功能
 * @param {String} type 打开还是关闭筛选功能  open-打开筛选功能，返回当前筛选的范围对象；close-关闭筛选功能，返回关闭前筛选的范围对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeFilter(type, options = {}) {
    let typeValues = ['open', 'close'];

    if (!typeValues.includes(type)) {
        return tooltip.info("The type parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    if (type == 'open') {
        file.filter_select = range;

        if (file.index == Store.currentSheetIndex) {
            createFilterOptions(range, file.filter);
        }

        return {
            "row": range.row,
            "column": range.column
        };
    }
    else if (type == 'close') {
        let luckysheet_filter_save = $.extend(true, {}, file.filter_select);

        file.filter_select = null;

        $("#luckysheet-filter-selected-sheet" + file.index).remove();
        $("#luckysheet-filter-options-sheet" + file.index).remove();

        return {
            "row": luckysheet_filter_save.row,
            "column": luckysheet_filter_save.column
        };
    }
}

/**
 * 为指定索引的工作表，选定的范围设定合并单元格
 * @param {String} type 合并类型 all-全部合并  horizontal-水平合并  vertical-垂直合并
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeMerge(type, options = {}) {
    let typeValues = ['all', 'horizontal', 'vertical'];
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'all\', \'horizontal\', \'vertical\']', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        data = $.extend(true, [], file.data);

    if (data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info('Incorrect selection format', '');
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
            return tooltip.info('Incorrect selection format', '');
        }

        range = [{
            "row": range.row,
            "column": range.column
        }]
    }

    //不能合并重叠区域
    if (selectIsOverlap(range)) {
        return tooltip.info('Cannot merge overlapping range', '');
    }

    //选区是否含有 部分合并单元格
    if (cfg["merge"] != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info('Cannot perform this operation on partially merged cells', '');
        }
    } else {
        cfg.merge = {}
    }

    //选区是否含有 合并的单元格
    let isHasMc = false;

    for (let i = 0; i < range.length; i++) {
        let r1 = range[i].row[0],
            r2 = range[i].row[1];
        let c1 = range[i].column[0],
            c2 = range[i].column[1];

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (getObjType(cell) == "object" && ("mc" in cell)) {
                    isHasMc = true;
                    break;
                }
            }

            if (isHasMc) {
                break;
            }
        }
    }

    if (isHasMc) {//选区有合并单元格（选区都执行 取消合并）
        cancelRangeMerge({
            range: range,
            order: order
        })
    }
    else {
        for (let i = 0; i < range.length; i++) {
            let r1 = range[i].row[0],
                r2 = range[i].row[1];
            let c1 = range[i].column[0],
                c2 = range[i].column[1];

            if (r1 == r2 && c1 == c2) {
                continue;
            }

            if (type == "all") {
                let fv = {}, isfirst = false;

                for (let r = r1; r <= r2; r++) {
                    for (let c = c1; c <= c2; c++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r1, "c": c1 } };
                    }
                }

                data[r1][c1] = fv;
                data[r1][c1].mc = { "r": r1, "c": c1, "rs": r2 - r1 + 1, "cs": c2 - c1 + 1 };

                cfg["merge"][r1 + "_" + c1] = { "r": r1, "c": c1, "rs": r2 - r1 + 1, "cs": c2 - c1 + 1 };
            }
            else if (type == "vertical") {
                for (let c = c1; c <= c2; c++) {
                    let fv = {}, isfirst = false;

                    for (let r = r1; r <= r2; r++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r1, "c": c } };
                    }

                    data[r1][c] = fv;
                    data[r1][c].mc = { "r": r1, "c": c, "rs": r2 - r1 + 1, "cs": 1 };

                    cfg["merge"][r1 + "_" + c] = { "r": r1, "c": c, "rs": r2 - r1 + 1, "cs": 1 };
                }
            }
            else if (type == "horizontal") {
                for (let r = r1; r <= r2; r++) {
                    let fv = {}, isfirst = false;

                    for (let c = c1; c <= c2; c++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r, "c": c1 } };
                    }

                    data[r][c1] = fv;
                    data[r][c1].mc = { "r": r, "c": c1, "rs": 1, "cs": c2 - c1 + 1 };

                    cfg["merge"][r + "_" + c1] = { "r": r, "c": c1, "rs": 1, "cs": c2 - c1 + 1 };
                }
            }
        }

        if (order == curSheetOrder) {
            if (Store.clearjfundo) {
                Store.jfundo.length = 0;
                Store.jfredo.push({
                    "type": "mergeChange",
                    "sheetIndex": file.index,
                    "data": $.extend(true, [], file.data),
                    "curData": data,
                    "range": range,
                    "config": $.extend(true, {}, file.config),
                    "curConfig": cfg
                });
            }

            Store.clearjfundo = false;
            jfrefreshgrid(data, range, { "cfg": cfg });
            Store.clearjfundo = true;
        }
        else {
            file.data = data;
            file.config = cfg;
        }
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定索引的工作表，选定的范围取消合并单元格
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function cancelRangeMerge(options = {}) {
    let curRange = Store.luckysheet_select_save,
        curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        data = $.extend(true, [], file.data);

    if (data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info('Incorrect selection format', '');
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
            return tooltip.info('Incorrect selection format', '');
        }

        range = [{
            "row": range.row,
            "column": range.column
        }]
    }

    //不能合并重叠区域
    if (selectIsOverlap(range)) {
        return tooltip.info('Cannot merge overlapping range', '');
    }

    //选区是否含有 部分合并单元格
    if (cfg["merge"] != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info('Cannot perform this operation on partially merged cells', '');
        }
    }

    for (let i = 0; i < range.length; i++) {
        let r1 = range[i].row[0],
            r2 = range[i].row[1];
        let c1 = range[i].column[0],
            c2 = range[i].column[1];

        if (r1 == r2 && c1 == c2) {
            continue;
        }

        let fv = {};

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell != null && cell.mc != null) {
                    let mc_r = cell.mc.r, mc_c = cell.mc.c;

                    if ("rs" in cell.mc) {
                        delete cell.mc;
                        delete cfg["merge"][mc_r + "_" + mc_c];

                        fv[mc_r + "_" + mc_c] = $.extend(true, {}, cell);
                    }
                    else {
                        // let cell_clone = fv[mc_r + "_" + mc_c];
                        let cell_clone = JSON.parse(JSON.stringify(fv[mc_r + "_" + mc_c]));

                        delete cell_clone.v;
                        delete cell_clone.m;
                        delete cell_clone.ct;
                        delete cell_clone.f;
                        delete cell_clone.spl;

                        data[r][c] = cell_clone;
                    }
                }
            }
        }
    }

    if (order == curSheetOrder) {
        if (Store.clearjfundo) {
            Store.jfundo.length = 0;
            Store.jfredo.push({
                "type": "mergeChange",
                "sheetIndex": file.index,
                "data": $.extend(true, [], file.data),
                "curData": data,
                "range": range,
                "config": $.extend(true, {}, file.config),
                "curConfig": cfg
            });
        }

        Store.clearjfundo = false;
        jfrefreshgrid(data, range, { "cfg": cfg });
        Store.clearjfundo = true;
    }
    else {
        file.data = data;
        file.config = cfg;
    }
}

/**
 * 为指定索引的工作表，选定的范围开启排序功能，返回选定范围排序后的数据。
 * @param {String} type 排序类型 asc-升序 desc-降序
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSort(type, options = {}) {
    let typeValues = ['asc', 'desc']
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'asc\', \'desc\'', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        fileData = $.extend(true, [], file.data);

    if (fileData.length == 0) {
        fileData = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().sort.noRangeError, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    let r1 = range.row[0],
        r2 = range.row[1],
        c1 = range.column[0],
        c2 = range.column[1];

    let hasMc = false; //Whether the sort selection has merged cells
    let data = [];
    for (let r = r1; r <= r2; r++) {
        let data_row = [];
        for (let c = c1; c <= c2; c++) {
            if (fileData[r][c] != null && fileData[r][c].mc != null) {
                hasMc = true;
                break;
            }
            data_row.push(fileData[r][c]);
        }
        data.push(data_row);
    }

    if (hasMc) {
        tooltip.info(locale().sort.mergeError, "");
        return;
    }

    data = orderbydata([].concat(data), 0, type === 'asc');

    for (let r = r1; r <= r2; r++) {
        for (let c = c1; c <= c2; c++) {
            fileData[r][c] = data[r - r1][c - c1];
        }
    }

    let allParam = {};
    if (cfg["rowlen"] != null) {
        cfg = rowlenByRange(fileData, r1, r2, cfg);

        allParam = {
            "cfg": cfg,
            "RowlChange": true
        }
    }

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, [{ "row": [r1, r2], "column": [c1, c2] }], allParam);
    }
    else {
        file.data = fileData;
        file.config = cfg;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定索引的工作表，选定的范围开启多列自定义排序功能，返回选定范围排序后的数据。
 * @param {Boolean} hasTitle 数据是否具有标题行
 * @param {Array} sort 列设置，设置需要排序的列索引和排序方式，格式如：[{ i:0,sort:'asc' },{ i:1,sort:'des' }]
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSortMulti(hasTitle, sort, options = {}) {
    if (!sort || !(sort instanceof Array)) {
        return tooltip.info('The sort parameter is invalid.', '')
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        fileData = $.extend(true, [], file.data);

    if (fileData.length == 0) {
        fileData = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().sort.noRangeError, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    let r1 = range.row[0],
        r2 = range.row[1],
        c1 = range.column[0],
        c2 = range.column[1];

    let str;
    if (hasTitle) {
        str = r1 + 1;
    } else {
        str = r1;
    }

    let hasMc = false; //Whether the sort selection has merged cells
    let data = [];
    for (let r = str; r <= r2; r++) {
        let data_row = [];
        for (let c = c1; c <= c2; c++) {
            if (fileData[r][c] != null && fileData[r][c].mc != null) {
                hasMc = true;
                break;
            }
            data_row.push(fileData[r][c]);
        }
        data.push(data_row);
    }

    if (hasMc) {
        tooltip.info(locale().sort.mergeError, "");
        return;
    }

    sort.forEach(sortItem => {
        let i = sortItem.i;
        i -= c1;
        data = orderbydata([].concat(data), i, sortItem.sort === 'asc');
    })

    for (let r = str; r <= r2; r++) {
        for (let c = c1; c <= c2; c++) {
            fileData[r][c] = data[r - str][c - c1];
        }
    }

    let allParam = {};
    if (cfg["rowlen"] != null) {
        cfg = rowlenByRange(fileData, str, r2, cfg);

        allParam = {
            "cfg": cfg,
            "RowlChange": true
        }
    }

    if (file.index === Store.currentSheetIndex) {
        jfrefreshgrid(fileData, [{ "row": [str, r2], "column": [c1, c2] }], allParam);
    }
    else {
        file.data = fileData;
        file.config = cfg;
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 *  为指定索引的工作表，选定的范围开启条件格式，根据设置的条件格式规则突出显示部分单元格，返回开启条件格式后的数据。
 * @param {String} conditionName 条件格式规则类型
 * @param {Object} conditionValue 可以设置条件单元格或者条件值
 * @param {Object} options 可选参数
 * @param {Object} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormatDefault(conditionName, conditionValue, options = {}) {
    let conditionNameValues = [
        'greaterThan',
        'lessThan',
        'betweenness',
        'equal',
        'textContains',
        'occurrenceDate',
        'duplicateValue',
        'top10',
        'top10%',
        'last10',
        'last10%',
        'AboveAverage',
        'SubAverage'
    ];

    if (!conditionName || !conditionNameValues.includes(conditionName)) {
        return tooltip.info('The conditionName parameter is invalid.', '');
    }

    if (getObjType(conditionValue) != 'array' || conditionValue.length == 0) {
        return tooltip.info('The conditionValue parameter is invalid.', '');
    }

    let {
        format = {
            "textColor": "#000000",
            "cellColor": "#ff0000"
        },
        cellrange = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    cellrange = JSON.parse(JSON.stringify(cellrange));

    let file = Store.luckysheetfile[order];
    let data = file.data;

    if (data == null || data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    if (file == null) {
        return tooltip.info('Incorrect worksheet index', '');
    }

    const conditionformat_Text = locale().conditionformat;

    let conditionRange = [], conditionValue2 = [];

    if (conditionName == 'betweenness') {
        let v1 = conditionValue[0];
        let v2 = conditionValue[1];

        //条件值是否是选区
        let rangeArr1 = conditionformat.getRangeByTxt(v1);
        if (rangeArr1.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        }
        else if (rangeArr1.length == 1) {
            let r1 = rangeArr1[0].row[0], r2 = rangeArr1[0].row[1];
            let c1 = rangeArr1[0].column[0], c2 = rangeArr1[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v1 = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr1[0].row, "column": rangeArr1[0].column });
                conditionValue2.push(v1);
            }
            else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        }
        else if (rangeArr1.length == 0) {
            if (isNaN(v1) || v1 == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            }
            else {
                conditionValue2.push(v1);
            }
        }

        let rangeArr2 = conditionformat.getRangeByTxt(v2);
        if (rangeArr2.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        }
        else if (rangeArr2.length == 1) {
            let r1 = rangeArr2[0].row[0], r2 = rangeArr2[0].row[1];
            let c1 = rangeArr2[0].column[0], c2 = rangeArr2[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v2 = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr2[0].row, "column": rangeArr2[0].column });
                conditionValue2.push(v2);
            }
            else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        }
        else if (rangeArr2.length == 0) {
            if (isNaN(v2) || v2 == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            }
            else {
                conditionValue2.push(v2);
            }
        }
    }
    else if (conditionName == 'greaterThan' || conditionName == 'lessThan' || conditionName == 'equal') {
        let v = conditionValue[0];

        //条件值是否是选区
        let rangeArr = conditionformat.getRangeByTxt(v);
        if (rangeArr.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        }
        else if (rangeArr.length == 1) {
            let r1 = rangeArr[0].row[0], r2 = rangeArr[0].row[1];
            let c1 = rangeArr[0].column[0], c2 = rangeArr[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr[0].row, "column": rangeArr[0].column });
                conditionValue2.push(v);
            }
            else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        }
        else if (rangeArr.length == 0) {
            if (isNaN(v) || v == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            }
            else {
                conditionValue2.push(v);
            }
        }
    }
    else if (conditionName == 'textContains') {
        let v = conditionValue[0];

        //条件值是否是选区
        let rangeArr = conditionformat.getRangeByTxt(v);
        if (rangeArr.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        }
        else if (rangeArr.length == 1) {
            let r1 = rangeArr[0].row[0], r2 = rangeArr[0].row[1];
            let c1 = rangeArr[0].column[0], c2 = rangeArr[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr[0].row, "column": rangeArr[0].column });
                conditionValue2.push(v);
            }
            else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        }
        else if (rangeArr.length == 0) {
            if (v == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            }
            else {
                conditionValue2.push(v);
            }
        }
    }
    else if (conditionName == 'occurrenceDate') {
        let v1 = conditionValue[0];
        let v2 = conditionValue[1];

        if (!isdatetime(v1) || !isdatetime(v2)) {
            return tooltip.info('The conditionValue parameter is invalid.', '');
        }

        let v;
        if (diff(v1, v2) > 0) {
            v = dayjs(v2).format("YYYY/MM/DD") + "-" + dayjs(v1).format("YYYY/MM/DD");
        }
        else {
            v = dayjs(v1).format("YYYY/MM/DD") + "-" + dayjs(v2).format("YYYY/MM/DD");
        }

        conditionValue2.push(v);
    }
    else if (conditionName == 'duplicateValue') {
        let v = conditionValue[0];

        if (v != '0' || v != '1') {
            return tooltip.info('The conditionValue parameter is invalid.', '');
        }

        conditionValue2.push(v);
    }
    else if (conditionName == 'top10' || conditionName == 'top10%' || conditionName == 'last10' || conditionName == 'last10%') {
        let v = conditionValue[0];

        if (parseInt(v) != v || parseInt(v) < 1 || parseInt(v) > 1000) {
            conditionformat.infoDialog(conditionformat_Text.pleaseEnterInteger, "");
            return;
        }

        conditionValue2.push(parseInt(v));
    }
    else if (conditionName == 'AboveAverage' || conditionName == 'SubAverage') {
        conditionValue2.push(conditionName);
    }

    if (!format.hasOwnProperty("textColor") || !format.hasOwnProperty("cellColor")) {
        return tooltip.info('The format parameter is invalid.', '');
    }

    if (getObjType(cellrange) == 'string') {
        cellrange = conditionformat.getRangeByTxt(cellrange);
    }
    else if (getObjType(cellrange) == 'object') {
        cellrange = [cellrange];
    }

    if (getObjType(cellrange) != 'array') {
        return tooltip.info('The cellrange parameter is invalid.', '');
    }

    let rule = {
        "type": "default",
        "cellrange": cellrange,
        "format": format,
        "conditionName": conditionName,
        "conditionRange": conditionRange,
        "conditionValue": conditionValue2
    };

    //保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    //保存当前的规则
    let ruleArr = file["luckysheet_conditionformat_save"] || [];
    ruleArr.push(rule);
    file["luckysheet_conditionformat_save"] = ruleArr;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    //刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    //发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 为指定索引的工作表，选定的范围开启条件格式，返回开启条件格式后的数据。
 * @param {String} type 条件格式规则类型
 * @param {Object} options 可选参数
 * @param {Array | String} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormat(type, options = {}) {
    let typeValues = [
        'dataBar',
        'colorGradation',
        'icons'
    ];

    if (!type || !typeValues.includes(type)) {
        return tooltip.info('The type parameter is invalid.', '');
    }

    let {
        format,
        cellrange = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    cellrange = JSON.parse(JSON.stringify(cellrange));
    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info('Incorrect worksheet index', '');
    }

    if (type == 'dataBar') {
        if (format == null) {
            format = ["#638ec6", "#ffffff"];
        }

        if (getObjType(format) != 'array' || format.length < 1 || format.length > 2) {
            return tooltip.info('The format parameter is invalid.', '');
        }
    }
    else if (type == 'colorGradation') {
        if (format == null) {
            format = ["rgb(99, 190, 123)", "rgb(255, 235, 132)", "rgb(248, 105, 107)"];
        }

        if (getObjType(format) != 'array' || format.length < 2 || format.length > 3) {
            return tooltip.info('The format parameter is invalid.', '');
        }
    }
    else if (type == 'icons') {
        if (format == null) {
            format = "threeWayArrowMultiColor";
        }

        let formatValues = [
            'threeWayArrowMultiColor',
            'threeTriangles',
            'fourWayArrowMultiColor',
            'fiveWayArrowMultiColor',
            'threeWayArrowGrayColor',
            'fourWayArrowGrayColor',
            'fiveWayArrowGrayColor',
            'threeColorTrafficLightRimless',
            'threeSigns',
            'greenRedBlackGradient',
            'threeColorTrafficLightBordered',
            'fourColorTrafficLight',
            'threeSymbolsCircled',
            'tricolorFlag',
            'threeSymbolsnoCircle',
            'threeStars',
            'fiveQuadrantDiagram',
            'fiveBoxes',
            'grade4',
            'grade5'
        ];

        if (getObjType(format) != 'string' || !formatValues.includes(format)) {
            return tooltip.info('The format parameter is invalid.', '');
        }

        switch (format) {
            case 'threeWayArrowMultiColor':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 0
                };
                break;
            case 'threeTriangles':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 1
                };
                break;
            case 'fourWayArrowMultiColor':
                format = {
                    "len": 4,
                    "leftMin": 0,
                    "top": 2
                };
                break;
            case 'fiveWayArrowMultiColor':
                format = {
                    "len": 5,
                    "leftMin": 0,
                    "top": 3
                };
                break;
            case 'threeWayArrowGrayColor':
                format = {
                    "len": 3,
                    "leftMin": 5,
                    "top": 0
                };
                break;
            case 'fourWayArrowGrayColor':
                format = {
                    "len": 4,
                    "leftMin": 5,
                    "top": 1
                };
                break;
            case 'fiveWayArrowGrayColor':
                format = {
                    "len": 5,
                    "leftMin": 5,
                    "top": 2
                };
                break;
            case 'threeColorTrafficLightRimless':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 4
                };
                break;
            case 'threeSigns':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 5
                };
                break;
            case 'greenRedBlackGradient':
                format = {
                    "len": 4,
                    "leftMin": 0,
                    "top": 6
                };
                break;
            case 'threeColorTrafficLightBordered':
                format = {
                    "len": 3,
                    "leftMin": 5,
                    "top": 4
                };
                break;
            case 'fourColorTrafficLight':
                format = {
                    "len": 4,
                    "leftMin": 5,
                    "top": 5
                };
                break;
            case 'threeSymbolsCircled':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 7
                };
                break;
            case 'tricolorFlag':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 8
                };
                break;
            case 'threeSymbolsnoCircle':
                format = {
                    "len": 3,
                    "leftMin": 5,
                    "top": 7
                };
                break;
            case 'threeStars':
                format = {
                    "len": 3,
                    "leftMin": 0,
                    "top": 9
                };
                break;
            case 'fiveQuadrantDiagram':
                format = {
                    "len": 5,
                    "leftMin": 0,
                    "top": 10
                };
                break;
            case 'fiveBoxes':
                format = {
                    "len": 5,
                    "leftMin": 0,
                    "top": 11
                };
                break;
            case 'grade4':
                format = {
                    "len": 4,
                    "leftMin": 5,
                    "top": 9
                };
                break;
            case 'grade5':
                format = {
                    "len": 5,
                    "leftMin": 5,
                    "top": 10
                };
                break;
        }
    }

    if (getObjType(cellrange) == 'string') {
        cellrange = conditionformat.getRangeByTxt(cellrange);
    }
    else if (getObjType(cellrange) == 'object') {
        cellrange = [cellrange];
    }

    if (getObjType(cellrange) != 'array') {
        return tooltip.info('The cellrange parameter is invalid.', '');
    }

    let rule = {
        "type": type,
        "cellrange": cellrange,
        "format": format
    };

    //保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    //保存当前的规则
    let ruleArr = file["luckysheet_conditionformat_save"] || [];
    ruleArr.push(rule);
    file["luckysheet_conditionformat_save"] = ruleArr;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    //刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    //发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 为指定下标的工作表，删除条件格式规则，返回被删除的条件格式规则
 * @param {Number} itemIndex 条件格式规则索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRangeConditionalFormat(itemIndex, options = {}) {
    if (!isRealNum(itemIndex)) {
        return tooltip.info('The itemIndex parameter is invalid.', '');
    }

    itemIndex = Number(itemIndex);

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info('The order parameter is invalid.', '');
    }

    let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);

    if (cdformat.length == 0) {
        return tooltip.info('This worksheet has no conditional format to delete', '');
    }
    else if (cdformat[itemIndex] == null) {
        return tooltip.info('The conditional format of the index cannot be found', '');
    }

    let cdformatItem = cdformat.splice(itemIndex, 1);

    //保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    //保存当前的规则
    file["luckysheet_conditionformat_save"] = cdformat;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    //刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    //发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return cdformatItem;
}


/**
 * 清除指定工作表指定单元格区域的内容，不同于删除选区的功能，不需要设定单元格移动情况
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 要清除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearRange(options = {}) {
    let {
        range = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    range = JSON.parse(JSON.stringify(range));
    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }]
    }
    else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let has_PartMC = false;

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

        if (has_PartMC) {
            break;
        }
    }

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let d = $.extend(true, [], file.data);

    if (d.length == 0) {
        d = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = d[r][c];

                if (getObjType(cell) == "object") {
                    delete cell["m"];
                    delete cell["v"];

                    if (cell["f"] != null) {
                        delete cell["f"];
                        formula.delFunctionGroup(r, c, file.index);

                        delete cell["spl"];
                    }

                    if (cell["ct"] != null && cell["ct"].t == 'inlineStr') {
                        delete cell["ct"];
                    }
                }
                else {
                    d[r][c] = null;
                }
            }
        }
    }

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(d, range);
    }
    else {
        file.data = d;
    }

    if (success && typeof success === 'function') {
        success();
    }
}


export function exportExcel(a, v) {
    return exportSheetExcel(a, v)
}

/**
 * 删除指定工作表指定单元格区域，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，单元格左移/上移
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 要删除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRange(move, options = {}) {
    let moveList = ['left', 'up'];

    if (!moveList.includes(move)) {
        return tooltip.info("The move parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    if (move == 'left') {
        luckysheetDeleteCell('moveLeft', str, edr, stc, edc, order);
    }
    else if (move == 'up') {
        luckysheetDeleteCell('moveUp', str, edr, stc, edc, order);
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 指定工作表指定单元格区域的数据进行矩阵操作，返回操作成功后的结果数据
 * @param {String} type 矩阵操作的类型
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixOperation(type, options = {}) {
    let typeValues = [
        'flipUpDown',               // 上下翻转
        'flipLeftRight',            // 左右翻转
        'flipClockwise',            // 顺时针旋转
        'flipCounterClockwise',     // 逆时针旋转
        'transpose',                // 转置
        'deleteZeroByRow',          // 按行删除两端0值
        'deleteZeroByColumn',       // 按列删除两端0值
        'removeDuplicateByRow',     // 按行删除重复值
        'removeDuplicateByColumn',  // 按列删除重复值
        'newMatrix'                 // 生产新矩阵
    ]

    if (!type || typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter is invalid.', '')
    }

    let curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        success
    } = { ...options }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().drag.noMulti, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    let getdata = getdatabyselection(range);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }

    let getdatalen, collen, arr1;
    switch (type) {
        case 'flipUpDown':
            for (let r = getdata.length - 1; r >= 0; r--) {
                let a = [];
                for (let c = 0; c < getdata[0].length; c++) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                    }
                    a.push(value);
                }
                arr.push(a);
            }
            break;
        case 'flipLeftRight':
            for (let r = 0; r < getdata.length; r++) {
                let a = [];
                for (let c = getdata[0].length - 1; c >= 0; c--) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                    }
                    a.push(value);
                }
                arr.push(a);
            }
            break;
        case 'flipClockwise':
            for (let c = 0; c < getdata[0].length; c++) {
                let a = [];
                for (let r = getdata.length - 1; r >= 0; r--) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                    }
                    a.push(value);
                }
                arr.push(a);
            }
            break;
        case 'flipCounterClockwise':
            for (let c = getdata[0].length - 1; c >= 0; c--) {
                let a = [];
                for (let r = 0; r < getdata.length; r++) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                    }
                    a.push(value);
                }
                arr.push(a);
            }
            break;
        case 'transpose':
            for (let c = 0; c < getdata[0].length; c++) {
                let a = [];
                for (let r = 0; r < getdata.length; r++) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                    }
                    a.push(value);
                }
                arr.push(a);
            }
            break;
        case 'deleteZeroByRow':
            getdatalen = getdata[0].length;
            for (let r = 0; r < getdata.length; r++) {
                let a = [], stdel = true, eddel = true;
                for (let c = 0; c < getdatalen; c++) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                        if ((value.v == "0" || value.v == 0) && stdel) {
                            continue;
                        }
                        else {
                            stdel = false;
                        }
                    }
                    a.push(value);
                }

                let a1 = [];
                if (a.length == getdatalen) {
                    a1 = a;
                } else {
                    for (let c = a.length - 1; c >= 0; c--) {
                        let value = "";
                        if (a[c] != null) {
                            value = a[c];
                            if ((value.v == "0" || value.v == 0) && eddel) {
                                continue;
                            }
                            else {
                                eddel = false;
                            }
                        }
                        a1.unshift(value);
                    }

                    let l = getdatalen - a1.length;
                    for (let c1 = 0; c1 < l; c1++) {
                        a1.push("");
                    }
                }
                arr.push(a1);
            }
            break;
        case 'deleteZeroByColumn':
            getdatalen = getdata.length;
            collen = getdata[0].length;
            for (let c = 0; c < collen; c++) {
                let a = [], stdel = true, eddel = true;
                for (let r = 0; r < getdatalen; r++) {
                    let value = "";
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];
                        if ((value.v == "0" || value.v == 0) && stdel) {
                            continue;
                        }
                        else {
                            stdel = false;
                        }
                    }
                    a.push(value);
                }

                let a1 = [];
                if (a.length == getdatalen) {
                    a1 = a;
                }
                else {
                    for (let r = a.length - 1; r >= 0; r--) {
                        let value = "";
                        if (a[r] != null) {
                            value = a[r];
                            if ((value.v == "0" || value.v == 0) && eddel) {
                                continue;
                            }
                            else {
                                eddel = false;
                            }
                        }
                        a1.unshift(value);
                    }

                    let l = getdatalen - a1.length;
                    for (let r1 = 0; r1 < l; r1++) {
                        a1.push("");
                    }
                }
                arr.push(a1);
            }

            arr1 = [];
            for (let c = 0; c < arr[0].length; c++) {
                let a = [];
                for (let r = 0; r < arr.length; r++) {
                    let value = "";
                    if (arr[r] != null && arr[r][c] != null) {
                        value = arr[r][c];
                    }
                    a.push(value);
                }
                arr1.push(a);
            }
            break;
        case 'removeDuplicateByRow':
            getdatalen = getdata[0].length;
            for (let r = 0; r < getdata.length; r++) {
                let a = [], repeat = {};

                for (let c = 0; c < getdatalen; c++) {
                    let value = null;
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];

                        if (value.v in repeat) {
                            repeat[value.v].push(value);
                        }
                        else {
                            repeat[value.v] = [];
                            repeat[value.v].push(value);
                        }
                    }
                }

                for (let c = 0; c < getdatalen; c++) {
                    let value = null;
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];

                        if (repeat[value.v].length == 1) {
                            a.push(value);
                        }
                    }
                }

                let l = getdatalen - a.length;
                for (let c1 = 0; c1 < l; c1++) {
                    a.push(null);
                }
                arr.push(a);
            }
            break;
        case 'removeDuplicateByColumn':
            collen = getdata[0].length;
            getdatalen = getdata.length;
            for (let c = 0; c < collen; c++) {
                let a = [], repeat = {};

                for (let r = 0; r < getdatalen; r++) {
                    let value = null;
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];

                        if (value.v in repeat) {
                            repeat[value.v].push(value);
                        }
                        else {
                            repeat[value.v] = [];
                            repeat[value.v].push(value);
                        }
                    }
                }

                for (let r = 0; r < getdatalen; r++) {
                    let value = null;
                    if (getdata[r] != null && getdata[r][c] != null) {
                        value = getdata[r][c];

                        if (repeat[value.v].length == 1) {
                            a.push(value);
                        }
                    }
                }

                a1 = a;
                let l = getdatalen - a1.length;
                for (let r1 = 0; r1 < l; r1++) {
                    a1.push(null);
                }
                arr.push(a1);
            }

            arr1 = [];
            for (let c = 0; c < arr[0].length; c++) {
                let a = [];
                for (let r = 0; r < arr.length; r++) {
                    let value = null;
                    if (arr[r] != null && arr[r][c] != null) {
                        value = arr[r][c];
                    }
                    a.push(value);
                }
                arr1.push(a);
            }
            break;
        case 'newMatrix':
            // TODO
            console.log("TODO")
            break;
    }
    editor.controlHandler(arr, range)

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 指定工作表指定单元格区域的数据进行矩阵计算，返回计算成功后的结果数据
 * @param {String} type 计算方式
 * @param {Number} number 计算数值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixCalculation(type, number, options = {}) {
    let typeValues = [
        'plus',     // 加
        'minus',    // 减
        'multiply', // 乘
        'divided',  // 除
        'power',    // 幂
        'root',     // 次方根
        'log'       // 对数log
    ]

    if (!type || typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter is invalid.', '')
    }

    if (!isRealNum(number)) {
        return tooltip.info('The number parameter is invalid.', '')
    }

    let curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        success
    } = { ...options }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().drag.noMulti, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range)
    }

    let getdata = getdatabyselection(range);
    if (getdata.length == 0) {
        return;
    }

    let arr = [];
    for (let r = 0; r < getdata.length; r++) {
        let a = [];
        for (let c = 0; c < getdata[0].length; c++) {
            let value = "";
            if (getdata[r] != null && getdata[r][c] != null) {
                value = getdata[r][c];
                if (parseInt(value) != null && getdata[r][c].ct != undefined && getdata[r][c].ct.t == "n") {
                    if (type == "minus") {
                        value.v = value.v - number;
                    }
                    else if (type == "multiply") {
                        value.v = value.v * number;
                    }
                    else if (type == "divided") {
                        value.v = numFormat(value.v / number, 4);
                    }
                    else if (type == "power") {
                        value.v = Math.pow(value.v, number);
                    }
                    else if (type == "root") {
                        if (number == 2) {
                            value.v = numFormat(Math.sqrt(value.v), 4);
                        }
                        else if (number == 3 && Math.cbrt) {
                            value.v = numFormat(Math.cbrt(value.v), 4);
                        }
                        else {
                            value.v = numFormat(jfnqrt(value.v, number), 4);
                        }
                    }
                    else if (type == "log") {
                        value.v = numFormat(Math.log(value.v) * 10000 / Math.log(Math.abs(number)), 4);
                    }
                    else {
                        value.v = value.v + number;
                    }

                    if (value.v == null) {
                        value.m = "";
                    }
                    else {
                        value.m = value.v.toString();
                    }
                }
            }
            a.push(value);
        }
        arr.push(a);
    }

    editor.controlHandler(arr, range);

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 新增一个sheet，返回新增的工作表对象
 * @param {Object} options 可选参数
 * @param {Object} options.sheetObject 新增的工作表的数据；默认值为空对象
 * @param {Number} options.order 新增的工作表索引；默认值为最后一个索引位置
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetAdd(options = {}) {
    let lastOrder = Store.luckysheetfile.length - 1;
    let {
        sheetObject = {},
        order = lastOrder,
        success
    } = { ...options }

    if (!isRealNum(order)) {
        return tooltip.info("Parameter is not a table index", "");
    }

    order = Number(order);

    let index = sheetmanage.generateRandomSheetIndex();
    // calcChain公式链里的index也要跟着变化
    if (sheetObject.calcChain && sheetObject.calcChain.length > 0) {
        sheetObject.calcChain.forEach((item) => {
            item.index = index
        })
    }
    let sheetname = sheetmanage.generateRandomSheetName(Store.luckysheetfile, false);
    if (!!sheetObject.name) {
        let sameName = false;

        for (let i = 0; i < Store.luckysheetfile.length; i++) {
            if (Store.luckysheetfile[i].name == sheetObject.name) {
                sameName = true;
                break;
            }
        }

        if (!sameName) {
            sheetname = sheetObject.name;
        }
    }

    $("#luckysheet-sheet-container-c").append(replaceHtml(sheetHTML, {
        "index": index,
        "active": "",
        "name": sheetname,
        "style": "",
        "colorset": ""
    }));

    let sheetconfig = {
        "name": "",
        "color": "",
        "status": "0",
        "order": "",
        "index": "",
        "celldata": [],
        "row": Store.defaultrowNum,
        "column": Store.defaultcolumnNum,
        "config": {},
        "pivotTable": null,
        "isPivotTable": false
    };
    sheetconfig = $.extend(true, sheetconfig, sheetObject);

    sheetconfig.index = index;
    sheetconfig.name = sheetname;
    sheetconfig.order = order;

    if (order <= 0) {
        let beforeIndex = Store.luckysheetfile[0].index;
        let beforeObj = $("#luckysheet-sheets-item" + beforeIndex);
        $("#luckysheet-sheets-item" + index).insertBefore(beforeObj);

        Store.luckysheetfile.splice(0, 0, sheetconfig);
    }
    else {
        if (order > Store.luckysheetfile.length) {
            order = Store.luckysheetfile.length;
        }

        let afterIndex = Store.luckysheetfile[order - 1].index;
        let afterObj = $("#luckysheet-sheets-item" + afterIndex);
        $("#luckysheet-sheets-item" + index).insertAfter(afterObj);

        Store.luckysheetfile.splice(order, 0, sheetconfig);
    }

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;
    })

    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + index).addClass("luckysheet-sheets-item-active");
    $("#luckysheet-cell-main").append('<div id="luckysheet-datavisual-selection-set-' + index + '" class="luckysheet-datavisual-selection-set"></div>');
    cleargridelement(true);

    server.saveParam("sha", null, $.extend(true, {}, sheetconfig));
    server.saveParam("shr", null, orders);

    if (Store.clearjfundo) {
        Store.jfundo.length = 0;
        let redo = {};
        redo["type"] = "addSheet";
        redo["sheetconfig"] = $.extend(true, {}, sheetconfig);
        redo["index"] = index;
        redo["currentSheetIndex"] = Store.currentSheetIndex;
        Store.jfredo.push(redo);
    }

    sheetmanage.changeSheetExec(index, false, true);

    if (success && typeof success === 'function') {
        success();
    }
    return sheetconfig;
}


/**
 * 删除指定下标的工作表，返回已删除的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetDelete(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (Store.luckysheetfile.length === 1) {
        return tooltip.info(locale().sheetconfig.noMoreSheet, "");
    }

    sheetmanage.deleteSheet(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}


/**
 * 复制指定下标的工作表到指定下标位置
 * @param {Object} options 可选参数
 * @param {Number} options.targetOrder 新复制的工作表目标下标位置；默认值为当前工作表下标的下一个下标位置（递增）
 * @param {Number} options.order 被复制的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetCopy(options = {}) {
    let {
        targetOrder,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (targetOrder == null) {
        targetOrder = order + 1;
    }

    if (!isRealNum(targetOrder)) {
        return tooltip.info("The targetOrder parameter is invalid.", "");
    }

    let copyindex = file.index;
    let index = sheetmanage.generateRandomSheetIndex();

    let copyjson = $.extend(true, {}, file);
    copyjson.order = Store.luckysheetfile.length;
    copyjson.index = index;
    copyjson.name = sheetmanage.generateCopySheetName(Store.luckysheetfile, copyjson.name);

    let colorset = '';
    if (copyjson.color != null) {
        colorset = '<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 0px; left: 0px; background-color: ' + copyjson.color + ';"></div>';
    }

    let afterObj = $("#luckysheet-sheets-item" + copyindex);
    if (isRealNum(targetOrder)) {
        afterObj = $("#luckysheet-sheets-item" + Store.luckysheetfile[targetOrder - 1].index);
    }

    $("#luckysheet-sheet-container-c").append(replaceHtml(sheetHTML, {
        "index": copyjson.index,
        "active": "",
        "name": copyjson.name,
        "order": copyjson.order,
        "style": "",
        "colorset": colorset
    }));
    $("#luckysheet-sheets-item" + copyjson.index).insertAfter(afterObj);
    Store.luckysheetfile.splice(targetOrder, 0, copyjson);

    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + index).addClass("luckysheet-sheets-item-active");
    $("#luckysheet-cell-main").append('<div id="luckysheet-datavisual-selection-set-' + index + '" class="luckysheet-datavisual-selection-set"></div>');
    cleargridelement(true);

    server.saveParam("shc", index, { "copyindex": copyindex, "name": copyjson.name });

    sheetmanage.changeSheetExec(index);
    sheetmanage.reOrderAllSheet();

    if (Store.clearjfundo) {
        Store.jfredo.push({
            "type": "copySheet",
            "copyindex": copyindex,
            "index": copyjson.index,
            "sheetIndex": copyjson.index
        });
    }
    else if (Store.jfredo.length > 0) {
        let jfredostr = Store.jfredo[Store.jfredo.length - 1];

        if (jfredostr.type == "copySheet") {
            jfredostr.index = copyjson.index;
            jfredostr.sheetIndex = copyjson.index;
        }
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return copyjson;
}


/**
 * 隐藏指定下标的工作表，返回被隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetHide(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    sheetmanage.setSheetHide(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}


/**
 * 取消隐藏指定下标的工作表，返回被取消隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetShow(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    sheetmanage.setSheetShow(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}


/**
 * 设置指定下标的工作表为当前工作表（激活态），即切换到指定的工作表，返回被激活的工作表对象
 * @param {Number} order 要激活的工作表下标
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetActive(order, options = {}) {
    if (order == null || !isRealNum(order) || Store.luckysheetfile[order] == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    let {
        success
    } = { ...options }

    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + file.index).addClass("luckysheet-sheets-item-active");

    sheetmanage.changeSheet(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);
    server.multipleRangeShow()
    return file;
}


/**
 * 修改工作表名称
 * @param {String} name 工作表名称
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetName(name, options = {}) {
    if (getObjType(name) != 'string' || name.toString().length == 0) {
        return tooltip.info("The name parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let oldtxt = file.name;
    file.name = name;

    $("#luckysheet-sheets-item" + file.index + " .luckysheet-sheets-item-name").text(name);

    server.saveParam("all", file.index, name, { "k": "name" });

    if (Store.clearjfundo) {
        let redo = {};
        redo["type"] = "sheetName";
        redo["sheetIndex"] = file.index;

        redo["oldtxt"] = oldtxt;
        redo["txt"] = name;

        Store.jfundo.length = 0;
        Store.jfredo.push(redo);
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 设置工作表名称处的颜色
 * @param {String} color 工作表颜色
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetColor(color, options = {}) {
    if (getObjType(color) != 'string' || color.toString().length == 0) {
        return tooltip.info("The color parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let oldcolor = file.color;
    file.color = color;

    $("#luckysheet-sheets-item" + file.index).find(".luckysheet-sheets-item-color").remove();
    $("#luckysheet-sheets-item" + file.index).append('<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 0px; left: 0px; background-color: ' + color + ';"></div>');

    server.saveParam("all", file.index, color, { "k": "color" });

    if (Store.clearjfundo) {
        let redo = {};
        redo["type"] = "sheetColor";
        redo["sheetIndex"] = file.index;

        redo["oldcolor"] = oldcolor;
        redo["color"] = color;

        Store.jfundo.length = 0;
        Store.jfredo.push(redo);
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 指定工作表向左边或右边移动一个位置，或者指定索引，返回指定的工作表对象
 * @param {String | Number} type 工作表移动方向或者移动的目标索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetMove(type, options = {}) {
    if (type != 'left' && type != 'right' && !isRealNum(type)) {
        return tooltip.info("Type parameter not available", "");
    }

    if (isRealNum(type)) {
        type = parseInt(type);
    }

    let curOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curOrder,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("ncorrect worksheet index", "");
    }

    let sheetIndex = file.index;

    if (type == 'left') {
        if (order == 0) {
            return;
        }

        let prevIndex = Store.luckysheetfile[order - 1].index;
        $("#luckysheet-sheets-item" + sheetIndex).insertBefore($("#luckysheet-sheets-item" + prevIndex));

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(order - 1, 0, file);
    }
    else if (type == 'right') {
        if (order == Store.luckysheetfile.length - 1) {
            return;
        }

        let nextIndex = Store.luckysheetfile[order + 1].index;
        $("#luckysheet-sheets-item" + sheetIndex).insertAfter($("#luckysheet-sheets-item" + nextIndex));

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(order + 1, 0, file);
    }
    else {
        if (type < 0) {
            type = 0;
        }

        if (type > Store.luckysheetfile.length - 1) {
            type = Store.luckysheetfile.length - 1;
        }

        if (type == order) {
            return;
        }

        if (type < order) {
            let prevIndex = Store.luckysheetfile[type].index;
            $("#luckysheet-sheets-item" + sheetIndex).insertBefore($("#luckysheet-sheets-item" + prevIndex));
        }
        else {
            let nextIndex = Store.luckysheetfile[type].index;
            $("#luckysheet-sheets-item" + sheetIndex).insertAfter($("#luckysheet-sheets-item" + nextIndex));
        }

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(type, 0, file);
    }

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;
    })

    server.saveParam("shr", null, orders);

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 重新排序所有工作表的位置，指定工作表顺序的数组。
 * @param {Array} orderList 工作表顺序，设置工作表的index和order来指定位置
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetOrder(orderList, options = {}) {
    if (orderList == null || orderList.length == 0) {
        return tooltip.info("Type orderList not available", "");
    }

    let orderListMap = {};
    orderList.forEach((item) => {
        orderListMap[item.index.toString()] = item.order;
    })

    Store.luckysheetfile.sort((x, y) => {
        let order_x = orderListMap[x.index.toString()];
        let order_y = orderListMap[y.index.toString()];

        if (order_x != null && order_y != null) {
            return order_x - order_y;
        }
        else if (order_x != null) {
            return -1;
        }
        else if (order_y != null) {
            return 1;
        }
        else {
            return 1;
        }
    })

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;

        if (i > 0) {
            let preIndex = arr[i - 1].index;
            $("#luckysheet-sheets-item" + item.index).insertAfter($("#luckysheet-sheets-item" + preIndex));
        }
    })

    server.saveParam("shr", null, orders);

    let {
        success
    } = { ...options }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 设置工作表缩放比例
 * @param {Number} zoom 工作表缩放比例，值范围为0.1 ~ 4；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetZoom(zoom, options = {}) {
    if (!isRealNum(zoom) || zoom < 0.1 || zoom > 4) {
        return tooltip.info("The zoom parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file["zoomRatio"] = zoom;

    server.saveParam("all", file.index, zoom, { "k": "zoomRatio" });

    if (file.index == Store.currentSheetIndex) {
        Store.zoomRatio = zoom;
        // 图片
        let currentSheet = sheetmanage.getSheetByIndex();
        imageCtrl.images = currentSheet.images;
        imageCtrl.allImagesShow();
        imageCtrl.init();

        zoomNumberDomBind();
        zoomRefreshView();
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 显示指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function showGridLines(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.showGridLines = true;

    if (file.index == Store.currentSheetIndex) {
        Store.showGridLines = true;

        setTimeout(function() {
            luckysheetrefreshgrid();
        }, 1);
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}


/**
 * 隐藏指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideGridLines(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.showGridLines = false;

    if (file.index == Store.currentSheetIndex) {
        Store.showGridLines = false;

        setTimeout(function() {
            luckysheetrefreshgrid();
        }, 1);
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}


/**
 * 刷新canvas
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function refresh(options = {}) {
    // luckysheetrefreshgrid();
    jfrefreshgrid();

    let {
        success
    } = { ...options }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 滚动当前工作表位置
 * @param {Object} options 可选参数
 * @param {Number} options.scrollLeft 横向滚动值
 * @param {Number} options.scrollTop 纵向滚动值
 * @param {Number} options.targetRow 纵向滚动到指定的行号
 * @param {Number} options.targetColumn 横向滚动到指定的列号
 * @param {Function} options.success 操作结束的回调函数
 */
export function scroll(options = {}) {
    let {
        scrollLeft,
        scrollTop,
        targetRow,
        targetColumn,
        success
    } = { ...options }

    if (scrollLeft != null) {
        if (!isRealNum(scrollLeft)) {
            return tooltip.info("The scrollLeft parameter is invalid.", "");
        }

        $("#luckysheet-scrollbar-x").scrollLeft(scrollLeft);
    }
    else if (targetColumn != null) {
        if (!isRealNum(targetColumn)) {
            return tooltip.info("The targetColumn parameter is invalid.", "");
        }

        let col = Store.visibledatacolumn[targetColumn],
            col_pre = targetColumn <= 0 ? 0 : Store.visibledatacolumn[targetColumn - 1];

        $("#luckysheet-scrollbar-x").scrollLeft(col_pre);
    }


    if (scrollTop != null) {
        if (!isRealNum(scrollTop)) {
            return tooltip.info("The scrollTop parameter is invalid.", "");
        }

        $("#luckysheet-scrollbar-y").scrollTop(scrollTop);
    }
    else if (targetRow != null) {
        if (!isRealNum(targetRow)) {
            return tooltip.info("The targetRow parameter is invalid.", "");
        }

        let row = Store.visibledatarow[targetRow],
            row_pre = targetRow <= 0 ? 0 : Store.visibledatarow[targetRow - 1];

        $("#luckysheet-scrollbar-y").scrollTop(row_pre);
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 根据窗口大小自动resize画布
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function resize(options = {}) {
    luckysheetsizeauto();

    let {
        success
    } = { ...options }

    if (success && typeof success === 'function') {
        success();
    }
}

export function getExcelRowColumn() {
    const sheetData = luckysheet.getSheetData();
    let objRowColumn = {
        row: [null, null], //行
        column: [null, null], //列
    };
    sheetData.forEach((item, index) => {
        //行数
        item.forEach((it, itemIndex) => {
            if (it !== null) {
                if (objRowColumn.row[0] == null) objRowColumn.row[0] = index; // row第一位
                objRowColumn.row[1] = index; //row第二位
                if (objRowColumn.column[0] == null)
                    objRowColumn.column[0] = itemIndex; //column第一位
                objRowColumn.column[1] = itemIndex; //column第二位
            }
        });
    });
    return objRowColumn;
}

export function printExcel() {
    let RowColumn = getExcelRowColumn() // 获取有值的行和列
    RowColumn.column[0] = 0 //因需要打印左边的边框，需重新设置
    luckysheet.setRangeShow(RowColumn) // 进行选区操作
    let src = luckysheet.getScreenshot(); // 生成base64图片
    let $img = `<img src=${src} style="width: 100%;position: absolute;left:-20px" />`;
    document.querySelector("#print-area").style.display = "block";
    document.querySelector("#print-html").innerHTML = $img;

}
/**
 * 返回指定选区截图后生成的base64格式的图片
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围，只能为单个选区；默认为当前选区
 */
export function getScreenshot(options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let visibledatarow = Store.visibledatarow;
    let visibledatacolumn = Store.visibledatacolumn;

    let scrollHeight, rh_height;
    if (str - 1 < 0) {
        scrollHeight = 0;
        rh_height = visibledatarow[edr];
    }
    else {
        scrollHeight = visibledatarow[str - 1];
        rh_height = visibledatarow[edr] - visibledatarow[str - 1];
    }

    let scrollWidth, ch_width;
    if (stc - 1 < 0) {
        scrollWidth = 0;
        ch_width = visibledatacolumn[edc];
    }
    else {
        scrollWidth = visibledatacolumn[stc - 1];
        ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1];
    }

    let newCanvas = $("<canvas>").attr({
        width: Math.ceil(ch_width * Store.devicePixelRatio),
        height: Math.ceil(rh_height * Store.devicePixelRatio)
    }).css({ width: ch_width, height: rh_height });

    luckysheetDrawMain(scrollWidth, scrollHeight, ch_width, rh_height, 1, 1, null, null, newCanvas);
    let ctx_newCanvas = newCanvas.get(0).getContext("2d");

    //补上 左边框和上边框
    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        0,
        Store.devicePixelRatio * rh_height
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();

    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        Store.devicePixelRatio * ch_width,
        0
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();

    let url = newCanvas.get(0).toDataURL("image/png");

    return url;
}


/**
 * 设置工作簿名称
 * @param {String} name 工作簿名称
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setWorkbookName(name, options = {}) {
    if (name == null || name.toString().length == 0) {
        return tooltip.info("The name parameter is invalid.", "");
    }

    $("#luckysheet_info_detail_input").val(name);

    let {
        success
    } = { ...options }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 获取工作簿名称
 * @param   {Object}    options         可选参数
 * @param   {Function}  options.success 操作结束的回调函数
 * @returns {String}    返回工作簿名称，如果读取失败则返回空字符串并弹窗提示
 */
export function getWorkbookName(options = {}) {

    let name = "";
    let element = $("#luckysheet_info_detail_input");

    if (element.length == 0) {

        tooltip.info('Failed to get workbook name, label loading failed!');
        return name;

    }

    name = $.trim(element.val());

    let {
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success()
        }
    }, 1)

    return name;
}


/**
 * 撤销当前操作，返回刚刚撤销的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function undo(options = {}) {
    let ctr = $.extend(true, {}, Store.jfredo[Store.jfredo.length - 1]);

    controlHistory.redo(new Event('custom'));
    luckysheetactiveCell();

    let {
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return ctr;
}


/**
 * 重做当前操作，返回刚刚重做的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function redo(options = {}) {
    let ctr = $.extend(true, {}, Store.jfundo[Store.jfundo.length - 1]);

    controlHistory.undo(new Event('custom'));
    luckysheetactiveCell();

    let {
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return ctr;
}


/**
 * 返回所有工作表配置
 */
export function getAllSheets() {
    let data = $.extend(true, [], Store.luckysheetfile);

    data.forEach((item, index, arr) => {
        if (item.data != null && item.data.length > 0) {
            item.celldata = sheetmanage.getGridData(item.data);
        }

        delete item.load;
        delete item.freezen;

    })

    return data;
}


/**
 * 根据index获取sheet页配置
 *
 * @param {Object} options 可选参数
 * @param {String} options.index 工作表index
 * @param {Number} options.order 工作表order
 * @param {String} options.name 工作表name
 */
export function getSheet(options = {}) {

    let {
        index,
        order,
        name
    } = { ...options };

    if (index != null) {
        return sheetmanage.getSheetByIndex(index);
    } else if (order != null) {
        return Store.luckysheetfile[order];
    } else if (name != null) {
        return sheetmanage.getSheetByName(name);
    }

    return sheetmanage.getSheetByIndex();

}

/**
 * 快捷返回指定工作表的数据
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getSheetData(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex)
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let data = $.extend(true, [], file.data);

    if (data == null || data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    return data;
}

/**
 * 快捷返回指定工作表的config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getConfig(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex)
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let config = $.extend(true, {}, file.config);

    return config;
}

/**
 * 快捷设置指定工作表config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setConfig(cfg, options = {}) {
    if (getObjType(cfg) != 'object') {
        return tooltip.info("The cfg parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.config = cfg;

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;

        if ("rowhidden" in cfg || "colhidden" in cfg || "rowlen" in cfg || "columnlen" in cfg) {
            jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
        }

        setTimeout(function() {
            luckysheetrefreshgrid();
        }, 1);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 返回所有表格数据结构的一维数组luckysheetfile
 */
export function getLuckysheetfile() {
    return getluckysheetfile();
}


/**
 * 指定工作表范围设置数据验证功能，并设置参数
 * @param {Object} optionItem 数据验证的配置信息
 * @param {String} optionItem.type 类型
 * @param {String | Null} optionItem.type2 条件类型
 * @param {String | Number} optionItem.value1 条件值1
 * @param {String | Number} optionItem.value2 条件值2
 * @param {Boolean} optionItem.checked 选中状态
 * @param {Boolean} optionItem.remote 自动远程获取选项
 * @param {Boolean} optionItem.prohibitInput 输入数据无效时禁止输入
 * @param {Boolean} optionItem.hintShow 选中单元格时显示提示语
 * @param {String} optionItem.hintText 提示语文本
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setDataVerification(optionItem, options = {}) {
    if (getObjType(optionItem) != 'object') {
        return tooltip.info("The optionItem parameter is invalid.", "");
    }

    let {
        type,
        type2 = null,
        value1 = '',
        value2 = '',
        remote = false,
        prohibitInput = false,
        hintShow = false,
        hintText = '',
        checked = false
    } = { ...optionItem }

    let typeValues = ["dropdown", "checkbox", "number", "number_integer", "number_decimal", "text_content", "text_length", "date", "validity"];
    let type2Values_1 = ["bw", "nb", "eq", "ne", "gt", "lt", "gte", "lte"];
    let type2Values_2 = ["include", "exclude", "equal"];
    let type2Values_3 = ["bw", "nb", "eq", "ne", "bf", "nbf", "af", "naf"];
    let type2Values_4 = ["card", "phone"];

    if (!typeValues.includes(type)) {
        return tooltip.info("The optionItem.type parameter is invalid.", "");
    }

    let dvText = locale().dataVerification;

    if (type == 'dropdown') {
        if (value1.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo1);
            return;
        }
    }
    else if (type == 'checkbox') {
        if (value1.length == 0 || value2.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo2);
            return;
        }
    }
    else if (type == 'number' || type == 'number_integer' || type == 'number_decimal') {
        if (!type2Values_1.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isRealNum(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isRealNum(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
                return;
            }

            if (Number(value2) < Number(value1)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
                return;
            }
        }
    }
    else if (type == 'text_content') {
        if (!type2Values_2.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (value1.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo5);
            return;
        }
    }
    else if (type == 'text_length') {
        if (!type2Values_1.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isRealNum(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isRealNum(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
                return;
            }

            if (Number(value2) < Number(value1)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
                return;
            }
        }
    }
    else if (type == 'date') {
        if (!type2Values_3.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isdatetime(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isdatetime(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
                return;
            }

            if (diff(value1, value2) > 0) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo7);
                return;
            }
        }
    }
    else if (type == 'validity') {
        if (!type2Values_4.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }
    }

    if (getObjType(remote) != 'boolean') {
        return tooltip.info("The optionItem.remote parameter is invalid.", "");
    }

    if (getObjType(prohibitInput) != 'boolean') {
        return tooltip.info("The optionItem.prohibitInput parameter is invalid.", "");
    }

    if (getObjType(hintShow) != 'boolean') {
        return tooltip.info("The optionItem.hintShow parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let item = {
        type: type,
        type2: type2,
        value1: value1,
        value2: value2,
        checked: checked,
        remote: remote,
        prohibitInput: prohibitInput,
        hintShow: hintShow,
        hintText: hintText,
    }

    let currentDataVerification = $.extend(true, {}, file.dataVerification);

    let data = $.extend(true, [], file.data);
    if (data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    for (let r = str; r <= edr; r++) {
        for (let c = stc; c <= edc; c++) {
            currentDataVerification[r + '_' + c] = item;

            if (type == 'checkbox') {
                item.checked ? setcellvalue(r, c, data, item.value1) : setcellvalue(r, c, data, item.value2);
            }
        }
    }

    if (file.index == Store.currentSheetIndex) {
        let historyDataVerification = $.extend(true, {}, file.dataVerification);

        if (type == 'checkbox') {
            dataVerificationCtrl.refOfCheckbox(historyDataVerification, currentDataVerification, Store.currentSheetIndex, data, range);
        }
        else {
            dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
        }
    }
    else {
        file.dataVerification = currentDataVerification;
        file.data = data;
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 指定工作表范围删除数据验证功能
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteDataVerification(options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let currentDataVerification = $.extend(true, {}, file.dataVerification);

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    for (let r = str; r <= edr; r++) {
        for (let c = stc; c <= edc; c++) {
            delete currentDataVerification[r + '_' + c];
        }
    }

    if (file.index == Store.currentSheetIndex) {
        let historyDataVerification = $.extend(true, {}, file.dataVerification);
        dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
    }
    else {
        file.dataVerification = currentDataVerification;
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 在指定的工作表中指定单元格位置插入图片
 * @param {String} src 图片src
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Number} options.rowIndex 要插入图片的单元格行下标；默认为0
 * @param {Number} options.colIndex 要插入图片的单元格列下标；默认为0
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertImage(src, options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        rowIndex,
        colIndex,
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (file.index == Store.currentSheetIndex) {
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        if (rowIndex == null) {
            rowIndex = last.row_focus || 0;
        }

        if (rowIndex < 0) {
            rowIndex = 0;
        }

        if (rowIndex > Store.visibledatarow.length) {
            rowIndex = Store.visibledatarow.length;
        }

        if (colIndex == null) {
            colIndex = last.column_focus || 0;
        }

        if (colIndex < 0) {
            colIndex = 0;
        }

        if (colIndex > Store.visibledatacolumn.length) {
            colIndex = Store.visibledatacolumn.length;
        }

        let left = colIndex == 0 ? 0 : Store.visibledatacolumn[colIndex - 1];
        let top = rowIndex == 0 ? 0 : Store.visibledatarow[rowIndex - 1];

        let image = new Image();
        image.onload = function() {
            let width = image.width,
                height = image.height;

            let img = {
                src: src,
                left: left,
                top: top,
                originWidth: width,
                originHeight: height
            }

            imageCtrl.addImgItem(img);

            if (success && typeof success === 'function') {
                success();
            }
        }
        image.src = src;
    }
    else {
        let images = file.images || {};
        let config = file.config;
        let zoomRatio = file.zoomRatio || 1;

        let rowheight = file.row;
        let visibledatarow = file.visibledatarow || [];
        if (visibledatarow.length === 0) {
            let rh_height = 0;

            for (let r = 0; r < rowheight; r++) {
                let rowlen = Store.defaultrowlen;

                if (config["rowlen"] != null && config["rowlen"][r] != null) {
                    rowlen = config["rowlen"][r];
                }

                if (config["rowhidden"] != null && config["rowhidden"][r] != null) {
                    visibledatarow.push(rh_height);
                    continue;
                }

                rh_height += Math.round((rowlen + 1) * zoomRatio);

                visibledatarow.push(rh_height); //行的临时长度分布
            }
        }

        let colwidth = file.column;
        let visibledatacolumn = file.visibledatacolumn || [];
        if (visibledatacolumn.length === 0) {
            let ch_width = 0;

            for (let c = 0; c < colwidth; c++) {
                let firstcolumnlen = Store.defaultcollen;

                if (config["columnlen"] != null && config["columnlen"][c] != null) {
                    firstcolumnlen = config["columnlen"][c];
                }

                if (config["colhidden"] != null && config["colhidden"][c] != null) {
                    visibledatacolumn.push(ch_width);
                    continue;
                }

                ch_width += Math.round((firstcolumnlen + 1) * zoomRatio);

                visibledatacolumn.push(ch_width);//列的临时长度分布
            }
        }

        if (rowIndex == null) {
            rowIndex = 0;
        }

        if (rowIndex < 0) {
            rowIndex = 0;
        }

        if (rowIndex > visibledatarow.length) {
            rowIndex = visibledatarow.length;
        }

        if (colIndex == null) {
            colIndex = 0;
        }

        if (colIndex < 0) {
            colIndex = 0;
        }

        if (colIndex > visibledatacolumn.length) {
            colIndex = visibledatacolumn.length;
        }

        let left = colIndex == 0 ? 0 : visibledatacolumn[colIndex - 1];
        let top = rowIndex == 0 ? 0 : visibledatarow[rowIndex - 1];

        let image = new Image();
        image.onload = function() {
            let img = {
                src: src,
                left: left,
                top: top,
                originWidth: image.width,
                originHeight: image.height
            }

            let width, height;
            let max = 400;

            if (img.originHeight < img.originWidth) {
                height = Math.round(img.originHeight * (max / img.originWidth));
                width = max;
            }
            else {
                width = Math.round(img.originWidth * (max / img.originHeight));
                height = max;
            }

            let imgItem = $.extend(true, {}, imageCtrl.imgItem);
            imgItem.src = img.src;
            imgItem.originWidth = img.originWidth;
            imgItem.originHeight = img.originHeight;
            imgItem.default.width = width;
            imgItem.default.height = height;
            imgItem.default.left = img.left;
            imgItem.default.top = img.top;
            imgItem.crop.width = width;
            imgItem.crop.height = height;

            let id = imageCtrl.generateRandomId();
            images[id] = imgItem;

            file.images = images;

            if (success && typeof success === 'function') {
                success();
            }
        }
        image.src = src;
    }
}


/**
 * 删除指定工作表中的图片
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {String | Array} options.idList 要删除图片的id集合，也可为字符串`"all"`，all为所有的字符串；默认为`"all"`
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteImage(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        idList = 'all',
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let images = file.images;

    if (images == null) {
        return tooltip.info("The worksheet has no pictures to delete.", "");
    }

    if (idList != 'all' && getObjType(idList) != 'array') {
        return tooltip.info("The idList parameter is invalid.", "");
    }

    if (getObjType(idList) == 'array') {
        idList.forEach(item => {
            delete images[item];
        })
    }
    else {
        images = null;
    }

    file.images = images;

    if (file.index == Store.currentSheetIndex) {
        if (imageCtrl.currentImgId != null && (idList == 'all' || idList.includes(imageCtrl.currentImgId))) {
            $("#luckysheet-modal-dialog-activeImage").hide();
            $("#luckysheet-modal-dialog-cropping").hide();
            $("#luckysheet-modal-dialog-slider-imageCtrl").hide();
        }

        imageCtrl.images = images;
        imageCtrl.allImagesShow();
        imageCtrl.init();
    }

    if (success && typeof success === 'function') {
        success();
    }
}


/**
 * 获取指定工作表的图片配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function getImageOption(options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    setTimeout(function() {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1)

    return file.images;
}


/**
 * data => celldata ，data二维数组数据转化成 {r, c, v}格式一维数组
 *
 * @param {Array} data 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToCellData(data, options = {}) {
    let {
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 0)

    return sheetmanage.getGridData(data)
}


/**
 * celldata => data ，celldata一维数组数据转化成表格所需二维数组
 *
 * @param {Array} celldata 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToData(celldata, options = {}) {
    let {
        success
    } = { ...options }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 0)

    return sheetmanage.buildGridData({
        celldata: celldata
    })
}

/**
 * 导出的json字符串可以直接当作`luckysheet.create(options)`初始化工作簿时的参数`options`使用
 *
 */
export function toJson() {

    const toJsonOptions = Store.toJsonOptions;

    // Workbook name
    toJsonOptions.title = $("#luckysheet_info_detail_input").val();

    toJsonOptions.data = getAllSheets();

    // row and column
    getluckysheetfile().forEach((file, index) => {

        if (file.data == undefined) {
            return;
        }
        toJsonOptions.data[index].row = getObjType(file.data) === 'array' ? file.data.length : 0;
        toJsonOptions.data[index].column = getObjType(file.data[0]) === 'array' ? file.data[0].length : 0;

    })

    return toJsonOptions;
}


/**
 * 传入目标语言，切换到对应的语言界面
 * @param {String} lang 可选参数；暂支持`"zh"`、`"en"`、`"es"`；默认为`"zh"`；
 */
export function changLang(lang = 'zh') {
    if (!['zh', 'en', 'es'].includes(lang)) {
        return tooltip.info("The lang parameter is invalid.", "");
    }

    let options = toJson();
    options.lang = lang;
    luckysheet.create(options);
}


/**
 * 关闭websocket连接
 */
export function closeWebsocket() {
    if (server.websocket == null) {
        return;
    }
    server.websocket.close(1000);
}


/**
 * 根据范围字符串转换为range数组
 * @param {String} txt 范围字符串
 */
export function getRangeByTxt(txt) {

    // 默认取当前第一个范围
    if (txt == null) {
        return {
            column: Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1].column,
            row: Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1].row
        }
    }

    const range = conditionformat.getRangeByTxt(txt);

    return {
        column: range[0].column,
        row: range[0].row
    };
}


/**
 * 根据范围数组转换为范围字符串
 * @param {Object | Array} range 范围数组
 */
export function getTxtByRange(range = Store.luckysheet_select_save) {

    // 单个范围
    if (getObjType(range) === 'object') {
        range = [range];
    }
    return conditionformat.getTxtByRange(range);
}

/**
 * 初始化分页器
 * @param {Object} config 分页器配置
 * @param {Number} config.pageIndex 当前的页码
 * @param {Number} config.pageSize 每页显示多少条数据
 * @param {Array} config.selectOption 选择每页的条数
 * @param {Number} config.total 总条数
 */
export function pagerInit(config) {
    const { prevPage, nextPage, total } = locale().button;
    $('#luckysheet-bottom-pager').remove()
    $('#luckysheet-sheet-content').after('<div id="luckysheet-bottom-pager" style="font-size: 14px; margin-left: 10px; display: inline-block;"></div>')
    $("#luckysheet-bottom-pager").sPage({
        page: config.pageIndex, //当前页码，必填
        total: config.total, //数据总条数，必填
        selectOption: config.selectOption, // 选择每页的行数，
        pageSize: config.pageSize, //每页显示多少条数据，默认10条
        showTotal: config.showTotal, // 是否显示总数，默认关闭：false
        showSkip: config.showSkip, //是否显示跳页，默认关闭：false
        showPN: config.showPN, //是否显示上下翻页，默认开启：true
        prevPage: config.prevPage || prevPage, //上翻页文字描述，默认"上一页"
        nextPage: config.nextPage || nextPage, //下翻页文字描述，默认"下一页"
        totalTxt: config.totalTxt || total + config.total, // 数据总条数文字描述，{total}为占位符，默认"总共：{total}"
        backFun: function(page) {
            page.pageIndex = page.page
            if (!method.createHookFunction("onTogglePager", page)) { return; }
        }
    });
}

/**
 * 刷新公式
 * @param {Function} success 回调函数
 */
export function refreshFormula(success) {
    formula.execFunctionGroupForce(true);
    luckysheetrefreshgrid()
    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    })
}

/**
 * 更新sheet数据
 * @param {Array} data 工作簿配置，可以包含多个表
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 *
 */
export function updataSheet(options = {}) {
    let { data, success } = options
    let files = Store.luckysheetfile
    for (let i = 0; i < data.length; i++) {
        for (let j = 0; j < files.length; j++) {
            if (files[j].index === data[i].index) {
                files[j] = data[i]
            }
        }
    }
    let file = files[sheetmanage.getSheetIndex(Store.currentSheetIndex)],
        sheetData = sheetmanage.buildGridData(file);
    file.data = sheetData

    if (!!file.isPivotTable) {
        Store.luckysheetcurrentisPivotTable = true;
        if (!isPivotInitial) {
            pivotTable.changePivotTable(index);
        }
    }
    else {
        Store.luckysheetcurrentisPivotTable = false;
        $("#luckysheet-modal-dialog-slider-pivot").hide();
        luckysheetsizeauto(false);
    }
    sheetmanage.mergeCalculation(file["index"]);
    sheetmanage.setSheetParam();
    setTimeout(function() {
        sheetmanage.showSheet();
        sheetmanage.restoreCache();
        formula.execFunctionGroupForce(luckysheetConfigsetting.forceCalculation);
        sheetmanage.restoreSheetAll(Store.currentSheetIndex);
        luckysheetrefreshgrid();
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);
    server.saveParam("shs", null, Store.currentSheetIndex);
}

/**
 * 刷新状态栏的状态
 * @param {Array}  data             操作数据
 * @param {Number} r                指定的行
 * @param {Number} c                指定的列
 * @param {Function} success        回调函数
 */
export function refreshMenuButtonFocus(data, r, c, success) {
    data = data || Store.flowdata;
    if (r == null && c == null) {
        /* 获取选取范围 */
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        r = last.row_focus || last.row[0];
        c = last.column_focus || last.column[0];
    }

    menuButton.menuButtonFocus(data, r, c);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    })
}

/**
 * 检查选区内所有cell指定类型的状态是否满足条件（主要是粗体、斜体、删除线和下划线等等）
 * @param {String}  type            类型
 * @param {String}  status          目标状态值
 */
export function checkTheStatusOfTheSelectedCells(type, status) {

    /* 获取选区内所有的单元格-扁平后的处理 */
    let cells = getRangeWithFlatten();

    let flag = cells.every(({ r, c }) => {
        let cell = Store.flowdata[r][c];
        if (cell == null) {
            return false;
        }
        return cell[type] == status;
    })

    return flag;
}



/**
 * 获取当前屏幕dpi
 * @returns dpi
 */
function getDPI() {
    var tempDiv = document.createElement("div");
    tempDiv.style.width = "1in";
    tempDiv.style.visibility = "hidden";
    document.body.appendChild(tempDiv);
    var dpi = tempDiv.offsetWidth;
    document.body.removeChild(tempDiv);
    return dpi;
}
/**
 * 毫米转像素
 * @param {int} mm 毫米
 * @param {int} dpi dpi
 * @returns 
 */
function mmToPixel(mm, dpi) {
    // 1 inch = 25.4 mm
    var inches = mm / 25.4;
    var pixels = inches * dpi;
    return Math.round(pixels);
}

function calculateA4PaperSize() {
    var dpi = getDPI();
    var width_mm = 210; // A4纸宽度，单位：毫米
    var height_mm = 297; // A4纸高度，单位：毫米
    var width_px = mmToPixel(width_mm, dpi);
    var height_px = mmToPixel(height_mm, dpi);
    return { width: width_px, height: height_px };
}
/**
 * 补上左边框和上边框
 */
const addLeftAndTopBorders = (ctx, rowIndexs, columnIndexs, borderInfo) => {
    let visibledatarow = Store.visibledatarow;
    let visibledatacolumn = Store.visibledatacolumn;
    function drawBorder(r, c, pos, border) {
        ctx.beginPath();
        const startTop = visibledatarow[r - 1] || 0;
        const startLeft = visibledatacolumn[c - 1] || 0;
        const cellheight = visibledatarow[r] - startTop;
        const cellWidth = visibledatacolumn[c] - startLeft;
        const devicePixelRatio = Store.devicePixelRatio;
        ctx.moveTo(startTop, startLeft);
        if (pos === 'left') {
            menuButton.setLineDash(ctx, border.style, 'v',
                startLeft * devicePixelRatio,
                startTop * devicePixelRatio,
                (startLeft + cellWidth) * devicePixelRatio,
                startTop * devicePixelRatio,
                true
            );
        }
        if (pos === 'top') {
            menuButton.setLineDash(ctx, border.style, 'h',
                startLeft * devicePixelRatio,
                startTop * devicePixelRatio,
                startLeft * devicePixelRatio,
                (startTop + cellheight) * devicePixelRatio,
                true
            );
        }
        ctx.strokeStyle = border.color;
        ctx.stroke();
        ctx.closePath();
    }
    function getCellBorderInfo(r, c, pos, filters) {
        let res = {};
        [...borderInfo || []].reverse().find(e => {
            if (e.rangeType === 'range') {
                const { row, column } = e.range[0];
                if (!filters.includes(e.borderType)) {
                    if (row[0] <= r && row[1] >= r && column[0] <= c && column[1] >= c) {
                        res = { color: e.color, style: e.style, borderType: e.borderType };
                        return true;
                    }
                }
                return false;
            }
            if (e.rangeType === 'cell') {
                const { col_index, row_index, l, t } = e.value || {};
                if (col_index === c && row_index === r) {
                    if (pos === 'top') {
                        if (t) {
                            res = t;
                            return true;
                        }
                    }
                    if (pos === 'left') {
                        if (l) {
                            res = t;
                            return true;
                        }
                    }
                }
            }
            return null;
        });
        return res;
    }
    for (let r = rowIndexs[0]; r <= rowIndexs[1]; r++) {
        for (let c = columnIndexs[0]; c <= columnIndexs[1]; c++) {
            if (c === 0) {
                const border = getCellBorderInfo(r, c, 'top',
                    ['border-left', 'border-right', 'border-bottom', 'border-inside', 'border-horizontal', 'border-vertical']);
                border && border.borderType !== 'border-none' && console.log(r, c, 'top', border)
                border && border.borderType !== 'border-none' && drawBorder(r, c, 'top', border);

            }
            if (r === 0) {
                const border = getCellBorderInfo(r, c, 'left',
                    ['border-top', 'border-right', 'border-bottom', 'border-inside', 'border-horizontal', 'border-vertical']);
                border && border.borderType !== 'border-none' && console.log(r, c, 'left', border)
                border && border.borderType !== 'border-none' && drawBorder(r, c, 'left', border);
            }
        }
    }
};
/**
 * 将选中区域进行截图，截图完成后调用指定委托函数，(修复支持插入的图片及图表截图)：ALAN-JI
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围，只能为单个选区；默认为当前选区
 * @param {function} action 委托执行函数，当所有绘图完成后，执行此委托函数（委托函数中可用于触发图片打印，或图片显示）
 */
export function getScreenshotWithImg(action, options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let visibledatarow = Store.visibledatarow;
    let visibledatacolumn = Store.visibledatacolumn;

    let scrollHeight, rh_height;
    if (str - 1 < 0) {
        scrollHeight = 0;
        rh_height = visibledatarow[edr];
    }
    else {
        scrollHeight = visibledatarow[str - 1];
        rh_height = visibledatarow[edr] - visibledatarow[str - 1];
    }

    //根据当前分辨率计算出A4纸宽度，高度（高度不用限制，默认会自动分页）
    let a4Size = calculateA4PaperSize();
    let scrollWidth, ch_width = a4Size.width;
    if (stc - 1 < 0) {
        scrollWidth = 0;
        // ch_width = visibledatacolumn[edc] < a4Size.width ? visibledatacolumn[edc] : a4Size.width;
        ch_width = visibledatacolumn[edc];
    }
    else {
        scrollWidth = visibledatacolumn[stc - 1];
        // ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1] < a4Size.width ? visibledatacolumn[edc] - visibledatacolumn[stc - 1] : a4Size.width;
        ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1];
    }

    let newCanvas = $("<canvas>").attr({
        width: Math.ceil(ch_width * Store.devicePixelRatio),
        height: Math.ceil(rh_height * Store.devicePixelRatio)
    }).css({ width: ch_width, height: rh_height });

    luckysheetDrawMain(scrollWidth, scrollHeight, ch_width, rh_height, 1, 1, null, null, newCanvas);


    //补上 左边框和上边框
    let ctx_newCanvas = newCanvas.get(0).getContext("2d");
    addLeftAndTopBorders(ctx_newCanvas, range.row, range.column, Store.config.borderInfo);
    // ctx_newCanvas.beginPath();
    // ctx_newCanvas.moveTo(
    //     0,
    //     0
    // );
    // ctx_newCanvas.lineTo(
    //     0,
    //     Store.devicePixelRatio * rh_height
    // );
    // ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    // ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    // ctx_newCanvas.stroke();
    // ctx_newCanvas.closePath();

    // ctx_newCanvas.beginPath();
    // ctx_newCanvas.moveTo(
    //     0,
    //     0
    // );
    // ctx_newCanvas.lineTo(
    //     Store.devicePixelRatio * ch_width,
    //     0
    // );
    // ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    // ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    // ctx_newCanvas.stroke();
    // ctx_newCanvas.closePath();

    //获取插入图片的元素，并在canvas上进行绘制
    if ($('#luckysheet-image-showBoxs')) {
        var imgs = $('#luckysheet-image-showBoxs img');
        imgs.each(function(i) {
            var parent = $(this).parent().parent();
            var left = parent.css("left").replace('px', '');
            var top = parent.css("top").replace('px', '');
            var width = parent.css("width").replace('px', '');
            var height = parent.css("height").replace('px', '');
            var img = new Image()
            img.src = $(this).attr("src");
            ctx_newCanvas.drawImage(img, left, top, width, height);
        });
    }
    //获取统计图元素，并在canvas上进行绘制
    let targetDoms = document.querySelectorAll('.luckysheet-modal-dialog.luckysheet-modal-dialog-chart.luckysheet-data-visualization-chart');
    var chartCount = 0;
    if (targetDoms && targetDoms.length > 0) {
        repaintChartScreenshot(newCanvas, ctx_newCanvas, targetDoms, action, 0, targetDoms.length);
    }
    if (!targetDoms || targetDoms.length == 0) {
        if (action) {
            action(newCanvas.get(0).toDataURL("image/png", 1));
        }
    }
}

/**
* 递归获取所有统计图，并重绘chart到截屏图上：ALAN-JI
* @param {object} newCanvas canvas画布元素对象
* @param {object} ctx_newCanvas 当前画布用于绘制新图的对象
* @param {NodeListOf} targetDoms 所有统计图Nodes集合
* @param {function} action 委托函数，当所有绘图完成后，执行此委托函数（委托函数中可用于触发图片打印，或图片显示）
* @param {int} index 当前统计图下标
* @param {int} length 统计图总集合长度
*/
export function repaintChartScreenshot(newCanvas, ctx_newCanvas, targetDoms, action, index, length) {
    let targetDom = targetDoms[index];
    var left = targetDom.style.left.replace('px', '');
    var top = targetDom.style.top.replace('px', '');
    var width = targetDom.style.width.replace('px', '');
    var height = targetDom.style.height.replace('px', '');

    // 此处是实现滚动元素长截图的关键 start
    let copyDom = targetDom.cloneNode(true)
    copyDom.setAttribute('id', 'copyDom')  // 更改id 避免与targetDom id重复
    copyDom.style.width = targetDom.scrollWidth + 'px'
    copyDom.style.height = targetDom.scrollHeight + 'px'
    var chartCanvas = targetDom.getElementsByTagName("canvas")[0];
    var canvasToImg = new Image();
    canvasToImg.src = chartCanvas.toDataURL('image/png', 1);
    canvasToImg.style = chartCanvas.style;
    copyDom.innerHTML = copyDom.innerHTML.replace(chartCanvas.outerHTML, canvasToImg.outerHTML)
    copyDom.style.zIndex = -50
    document.querySelector('body').appendChild(copyDom);
    // 此处是实现滚动元素长截图的关键 end

    /* 
    *如不需要长截图，或者要截取的元素无滚动即完全显示。
    *下方要截取的元素改为targetDom,并把copyDom相关代码删除即可 
    */
    // html2canvas截屏，屏幕有滚动条时截图为空
    html2canvas(copyDom, {
        backgroundColor: "transparent",
        allowTaint: true,
        useCORS: true,
        scale: window.devicePixelRatio * 3,
        logging: false,
        imageTimeout: 15000,
        removeContainer: true,
    }).then(canvas => {
        var chartImg = new Image()
        chartImg.src = canvas.toDataURL('image/png', 1);
        document.querySelector('body').removeChild(copyDom)
        chartImg.onload = function() {
            index++;
            ctx_newCanvas.drawImage(chartImg, left, top, width, height);
            if (index >= length && action) {
                action(newCanvas.get(0).toDataURL("image/png", 1));
            }
            else {
                //递归调用重绘图片
                repaintChartScreenshot(newCanvas, ctx_newCanvas, targetDoms, action, index, length);
            }
        }
    });
}
