import { ChapterData } from "./interface/ChapterData";
import { Element } from "./class/Element";
import { Lattice } from "./class/Lattice";
import { ElementType, FiveBaseElementTypes } from "./enum/ElementType";
import { PassType } from "./enum/PassType";
import { EffectType } from "./enum/EffectType";
import { Pool } from "./Pool";
import { RecoverName } from "./enum/RecoverName";
import { RectMask } from "./class/RectMask";
import { FallType } from "./interface/FallAniData";
import { GenerateLatData } from "./interface/GenerateLatData";
import { StateType } from "./enum/StateType";

//两种，0无棒棒糖，1有
const offsetYTwo: number[] = [];
const gameAreaHeightTwo: number[] = [];
//初始化适配位置，就因为棒棒糖的45偏移
function initClientScale() {
    var sysInfo;
    //@ts-ignore
    if (my) sysInfo = my.getSystemInfoSync()
    var stageHeight = sysInfo ? sysInfo.windowHeight / sysInfo.windowWidth * 750 :
        document.body.clientHeight / document.body.clientWidth * 750;
    var middleHeight = 80 * 9;
    var topHeight = 238;
    var botHeight = 140 + 20//210  //+10
    var smallestHeight = topHeight + middleHeight + botHeight;
    if (stageHeight < smallestHeight) {
        offsetYTwo[0] = topHeight;
        offsetYTwo[1] = topHeight;
        gameAreaHeightTwo[0] = middleHeight;
        gameAreaHeightTwo[1] = middleHeight + 45;
    } else {
        offsetYTwo[0] = topHeight + (stageHeight - smallestHeight) / 2;
        offsetYTwo[1] = topHeight + (stageHeight - smallestHeight - 45) / 2;
        gameAreaHeightTwo[0] = stageHeight - topHeight - botHeight;;
        gameAreaHeightTwo[1] = stageHeight - topHeight - botHeight;
    }
}

export class Tool {
    /**
     * 每格掉落时间
     * 掉落速度这个定
     */
    public static latDeltaTime: number = 90;
    /**
     * 总行数
     */
    public static rowNum = 9;
    /**
     * 总列数
     */
    public static colNum = 9;
    /**
     * 格子宽度
     */
    public static width = 80// 81.5;
    /**
     * 格子高度
     */
    public static height = 80// 81.5;
    //整体偏移X，为了下面初始化，这里不定义数值
    public static offsetX //= 15// 8.25;
    //整体偏移Y
    public static offsetY //= 275 //260;
    //游戏区域高度
    public static gameAreaHeight: number;
    /**
     * 圆角半径
     */
    public static radius = 10;
    /**
     * 单格子圆角背景宽度
     */
    // public static rectLatBgWidth = 80;
    // public static rectLatBgHeight = 76;
    /**
     * row和col转换到index的hashMap
     */
    private static rcToIndexMap = {};

    /**
     * index到row和col的hashMap
     */
    private static indexToRcMap: number[][] = [];
    /**
     * 行列位置，记录每个格子中心点的位置吧
     */
    private static rowColPositions = {};
    /**
     * 索引位置
     */
    private static indexPositions: number[][] = [];

    //给出10*10
    private static indexToRcMap10: number[][] = [];
    private static indexPositions10: number[][] = [];
    /**
     * 初始化数据
     * 提前的缓存数据，千万别修改，因为给出的数据都没有深拷贝过
     */
    public static init(isTwo: boolean = false, hasLollipop: boolean = false) {
        //初始化屏幕尺寸
        if (!offsetYTwo.length) initClientScale();
        //x的偏移
        var ooox: number = isTwo ? 55 : 15;// 49 : 8.25
        var oooy: number = hasLollipop ? offsetYTwo[1] : offsetYTwo[0];
        this.gameAreaHeight = hasLollipop ? gameAreaHeightTwo[1] : gameAreaHeightTwo[0];
        //如果都一致就返回
        if (this.offsetX == ooox && this.offsetY == oooy) return;
        this.offsetX = ooox;
        this.offsetY = oooy;
        //都是9*9
        //位置信息
        var spaceX = this.width,
            spaceY = this.height,
            offsetX = this.offsetX + this.width / 2,
            offsetY = this.offsetY + this.height / 2;
        for (var i = 0; i < this.rowNum; i++) {
            for (var j = 0; j < this.colNum; j++) {
                this.rcToIndexMap["" + i + j] = i * this.colNum + j;
                this.indexToRcMap[i * this.colNum + j] = [i, j];
                this.rowColPositions["" + i + j] = [offsetX + j * spaceX, offsetY + i * spaceY];
                this.indexPositions[i * this.colNum + j] = [offsetX + j * spaceX, offsetY + i * spaceY];
            }
        }

        //10*10的格子位置信息
        var rowNum = Tool.rowNum + 1;
        var colNum = Tool.colNum + 1;
        var offsetX = Tool.offsetX
        var offsetY = Tool.offsetY
        for (var i = 0; i < rowNum; i++) {
            for (var j = 0; j < colNum; j++) {
                // rcToIndexMap["" + i + j] = i * colNum + j;
                this.indexToRcMap10[i * colNum + j] = [i, j];
                this.indexPositions10[i * colNum + j] = [offsetX + j * Tool.width, offsetY + i * Tool.height];
            }
        }
    }
    /**
     * row和col获得index值
     * @param row 
     * @param col 
     */
    public static rcToIndex(row: number, col: number): number {
        var key = "" + row + col;
        return this.rcToIndexMap[key]
    }
    /**
     * index获得row和col，返回的是数组，0是row，1是col
     * @param index
     */
    public static indexToRc(index: number): number[] {
        return this.indexToRcMap[index]
    }
    /**
     * 根据row，col得到位置信息
     * @param row 
     * @param col 
     * @return 类似数组[111,222],0是x坐标，1是y坐标
     */
    public static getPositionByRc(row: number, col: number): number[] {
        var key = "" + row + col;
        return this.rowColPositions[key]
    }
    /**
     * 根据index得到位置信息
     * @param index 
     * @return 类似数组[111,222],0是x坐标，1是y坐标
     */
    public static getPositionByIndex(index: number): number[] {
        return this.indexPositions[index]
    }

    public static indexToRc10(index: number): number[] {
        return this.indexToRcMap10[index]
    }
    public static getPositionByIndex10(index: number): number[] {
        return this.indexPositions10[index]
    }

    /**
     * 根据9*9格子得到中间列数的奇偶性
     * 其实只是要知道左右两边空的列数
     * @param lattices 
     * @returns true为偶数，false为奇数
     */
    public static getColOddEven(lattices: number[]): boolean {
        var left: number = 0;
        for (var i = 0; i < Tool.colNum; i++) {
            //判断该列是否有格子,没有就加1，有就直接over
            var mark = false;
            for (var j = 0; j < Tool.rowNum; j++) {
                if (lattices[j * this.colNum + i]) {
                    mark = true;
                    break
                }
            }
            if (mark) {
                break
            } else {
                left++
            }
        }
        var right: number = 0;
        for (var i = Tool.colNum - 1; i >= 0; i--) {
            //判断该列是否有格子,没有就加1，有就直接over
            var mark = false;
            for (var j = 0; j < Tool.rowNum; j++) {
                if (lattices[j * this.colNum + i]) {
                    mark = true;
                    break
                }
            }
            if (mark) {
                break
            } else {
                right++
            }
        }
        return (left + right) % 2 != 0
    }


    /**
     * 判断相邻
     * @param a 
     * @param b 
     */
    public static checkNeb(a: Element, b: Element) {
        if (Math.abs(a.row - b.row) == 1 && Math.abs(a.column - b.column) == 0) {
            return true
        } else if (Math.abs(a.row - b.row) == 0 && Math.abs(a.column - b.column) == 1) {
            return true
        }
        return false
    }

    /**
     * 数组中插入一个数值，按顺序的
     * 数组是从小到大的
     * @param num 
     * @param arr 
     */
    public static insert(num, arr) {
        for (var i = arr.length - 1; i >= 0; i--) {
            if (num > arr[i]) {
                //在arr[i]后加num
                arr.splice(i + 1, 0, num);
                break
            }
        }
    }
    /**
     * 数组从大到小
     * @param num 
     * @param arr 
     */
    public static insertEX(num, arr) {
        for (var i = 0; i < arr.length; i++) {
            if (num > arr[i]) {
                //在arr[i]后加num
                arr.splice(i, 0, num);
                return
            }
        }
        arr.push(num)
    }
    /**
     * 判断格子是否能掉落，
     * @param lat 
     */
    public static judgeFall(lat: Lattice): boolean {
        //上方格子为null，或格子上元素为null
        if (!lat || !lat.element) {
            return false
        }
        //上方元素为石头，或元素为锁定，或者是果冻，或者鸡蛋，或者大节日元素，或者石门关闭
        else if (lat.element.type == ElementType.ROCK ||
            lat.element.hasState(StateType.LOCK) ||
            lat.element.type == ElementType.JELLY ||
            lat.element.type == ElementType.CHICKEN_EGG ||
            lat.element.type == ElementType.FESTIVALELE_BIG ||
            // lat.element.type == ElementType.FESTIVALELE_SMALL
            lat.doorClosed
        ) {
            return false
        }
        //剩下情况
        else {
            return true
        }
    }

    /**
     * 格子允许手动移动
     * 在允许掉落的基础上去掉，带毛球的，因为毛球能掉落，但是不能移动
     * @param lat 
     */
    public static judgeMove(lat: Lattice): boolean {
        if (this.judgeFall(lat) &&
            !lat.element.hasState(StateType.HAIRBALLBLACK) &&
            !lat.element.hasState(StateType.HAIRBALLGREY) &&
            !lat.element.hasState(StateType.HAIRBALLBROWN) &&
            lat.element.type != ElementType.FESTIVALELE_SMALL
        ) {
            return true
        }
        return false
    }

    /**
     * 判断格子是否可进行匹配，只有基础5种元素才能参与匹配
     * 不包括魔力鸟,不包括带毛球的
     * 包括笼子里的
     * @param lat 
     */
    public static judgeMatch(lat: Lattice) {
        if (Tool.judgeBaseEle(lat) && //是基础元素
            !lat.doorClosed &&  //石门非关闭
            lat.element.effectType != EffectType.MAGICLION &&  //特效不为魔力鸟
            !lat.element.hasState(StateType.HAIRBALLGREY) &&     //不带毛球， 下同
            !lat.element.hasState(StateType.HAIRBALLBLACK) &&
            !lat.element.hasState(StateType.HAIRBALLBROWN)
        ) {
            return true
        }
        return false
    }
    /**
     * 判断能执行消除的元素，
     * 包括所有特效和基本元素，石头，果冻，鸡蛋，节日元素，各种状态的
     * @param lat 
     */
    public static judgeEliminate(lat: Lattice) {
        //上方格子为null，或格子上元素为null
        if (!lat || !lat.element) {
            return false
        }
        //上方元素为冰淇淋 ，或 石门关闭
        else if (
            lat.element.type == ElementType.LOLLIPOP ||
            lat.doorClosed
        ) {
            return false
        }
        //剩下情况
        else {
            return true
        }
    }

    /**
     * 判断格子上是基础元素，包括各种状态的
     * 
     * @param lat 
     */
    public static judgeBaseEle(lat: Lattice) {
        if (lat &&
            lat.element &&
            FiveBaseElementTypes.indexOf(lat.element.type) >= 0 //在5种基本元素中
        ) {
            return true
        }
        return false
    }

    /**
     * 判断在打乱时，格子上元素是否能移动交换
     * @param lat 格子
     */
    public static judgeUpsetMove(lat: Lattice) {
        if (Tool.judgeBaseEle(lat) &&  //基础元素
            !lat.doorClosed &&  //石门非关闭
            !lat.element.hasState(StateType.LOCK) &&  //不带锁
            !lat.element.hasState(StateType.HAIRBALLGREY) &&  //不带毛球，下同
            !lat.element.hasState(StateType.HAIRBALLBLACK) &&
            !lat.element.hasState(StateType.HAIRBALLBROWN)
        ) {
            return true;
        }
        return false;
    }

    /**
     * 可放置节日元素
     * 必须是基础元素，且不带毛球状态
     * 毛球可能成为通关目标，不放
     * 
     * @param lat 
     */
    public static judgeSetFesEle(lat: Lattice) {
        if (Tool.judgeBaseEle(lat) &&  //基础元素
            !lat.doorClosed &&  //石门非关闭
            !lat.element.hasState(StateType.HAIRBALLGREY) &&  //不带毛球，下同
            !lat.element.hasState(StateType.HAIRBALLBLACK) &&
            !lat.element.hasState(StateType.HAIRBALLBROWN)
        ) {
            return true;
        }
        return false;
    }

    /**
     * 判断鸡蛋孵小鸡可飞入的索引
     * 基础元素，无任何状态，类型不为鸡，石门非关闭
     * @param lat 
     */
    public static judgeSetChicken(lat: Lattice): boolean {
        if (Tool.judgeBaseEle(lat) &&
            !lat.element.hasAnyState() &&
            lat.element.type !== ElementType.CHICKEN &&
            !lat.doorClosed
        ) {
            return true;
        }
        return false;
    }

    /**
     * 判断可被单魔力鸟效果消除的元素
     * 是基础元素，非石门关闭，非魔力鸟，（基础类型的判断自行添加）
     * @param lat 
     */
    public static judgeMagicEliminate(lat: Lattice): boolean {
        if (Tool.judgeBaseEle(lat) &&  //是基础元素
            !lat.doorClosed &&//石门非关闭状态
            lat.element.effectType != EffectType.MAGICLION  //不能是魔力鸟特效
        ) {
            return true;
        }
        return false
    }

    /**
     * 判断
     * 果冻可蔓延、
     * 毛球可分裂、
     * 毛球可跳动
     * 的方向格子索引，暂时都一致，不区分
     * 是基础元素，无任何状态，石门非关闭
     * @param lat 
     */
    public static judgeAiActionDirection(lat: Lattice): boolean {
        if (Tool.judgeBaseEle(lat) &&
            !lat.element.hasAnyState() &&
            !lat.doorClosed
        ) {
            return true;
        }
        return false;
    }

    /**
     * 判断元素是否可被手势选中和交换
     * 主要点击元素判断使用
     * @param ele 元素
     * @return 返回true为可选，false不可选
     */
    public static judgeChosen(ele: Element) {
        if (!ele ||
            ele.type == ElementType.ROCK ||
            ele.hasState(StateType.LOCK) ||
            ele.hasState(StateType.HAIRBALLGREY) ||
            ele.hasState(StateType.HAIRBALLBLACK) ||
            ele.hasState(StateType.HAIRBALLBROWN) ||
            ele.type == ElementType.JELLY ||
            ele.type == ElementType.CHICKEN_EGG ||
            ele.type == ElementType.FESTIVALELE_BIG ||
            ele.type == ElementType.FESTIVALELE_SMALL
        ) {
            return false;
        }
        return true
    }
    /**
     * 是否要算分数
     * 排除元素 石头，鸡蛋，果冻，节日元素
     * 排除状态 枷锁，毛球，因为存在时，元素不会消除
     * @param ele 元素
     */
    public static judgeHasScore(ele: Element): boolean {
        if (!ele ||
            ele.type == ElementType.ROCK ||
            ele.type == ElementType.JELLY ||
            ele.type == ElementType.CHICKEN_EGG ||
            ele.type == ElementType.LOLLIPOP ||
            ele.type == ElementType.FESTIVALELE_BIG ||
            ele.type == ElementType.FESTIVALELE_SMALL ||
            ele.hasState(StateType.LOCK) ||
            ele.hasState(StateType.HAIRBALLGREY) ||
            ele.hasState(StateType.HAIRBALLBLACK) ||
            ele.hasState(StateType.HAIRBALLBROWN)
        ) {
            return false;
        }
        return true
    }
    /**
     * 判断能否参与魔力鸟旋转的元素，不判断基础元素，因为这方法前必加判断
     * 无特效，无锁，无毛球，
     * @param ele 元素
     */
    public static judgeMagicRotate(ele: Element): boolean {
        if (!ele ||
            ele.effectType != null ||
            ele.hasState(StateType.LOCK) ||
            ele.hasState(StateType.HAIRBALLGREY) ||
            ele.hasState(StateType.HAIRBALLBLACK) ||
            ele.hasState(StateType.HAIRBALLBROWN)
        ) {
            return false;
        }
        return true
    }
    /**
     * 爆炸波及的格子索引
     */
    private static ExplosiveMatchArr = [
        // [0, -2],
        [-1, -1], [0, -1], [1, -1],
        /*[-2, 0],*/ [-1, 0], [1, 0], //[2, 0],
        [-1, 1], [0, 1], [1, 1],
        // [0, 2],
    ]
    /**
     * 获取爆炸时的周边索引
     */
    public static getExplosiveIndexes(curLat: Lattice, lats: Lattice[]): number[] {
        var arr = []
        Tool.ExplosiveMatchArr.forEach((e) => {
            var col = curLat.column + e[0]
            var row = curLat.row + e[1]
            var index = Tool.rcToIndex(row, col);
            if (Tool.judgeEliminate(lats[index])) arr.push(index)
        })
        return arr
    }
    /**
     * 检测死图，需要提示
     * 需要返回一组提示 两个能互相交换元素,数组
     * 有返回证明不是死图，
     */
    public static dieMapCheck(lattices: Lattice[]): Element[] {
        var judgeMatch = Tool.judgeMatch;
        //记录有特效的元素,下面特效检测用
        var effectElements = []
        //检测普通元素,
        var lat2, lat3;//额外两个元素声明
        for (var i = Tool.colNum * Tool.rowNum - 1; i >= 0; i--) {
            var rc = Tool.indexToRc(i);
            var row = rc[0];
            var col = rc[1];
            var lat = lattices[i];
            //如果自身格子空或不能交换，跳入下一个
            if (!this.judgeMove(lat)) continue
            if (lat.element.effectType != null) {
                effectElements.push(lat.element);
            }
            //与下交换
            var latDown = lattices[i + Tool.colNum];
            //能交换，并且类型不一致
            if (this.judgeMove(latDown) && lat.element.type != latDown.element.type) {
                //判断向下，+2，+3 lat
                lat2 = lattices[i + Tool.colNum * 2];
                lat3 = lattices[i + Tool.colNum * 3];
                if (judgeMatch(lat2) &&
                    judgeMatch(lat3) &&
                    lat.element.type == lat2.element.type &&
                    lat.element.type == lat3.element.type) {
                    return [lat.element, latDown.element];
                }
                //向上判断 -1 -2 latDown
                lat2 = lattices[i - Tool.colNum];
                lat3 = lattices[i - Tool.colNum * 2];
                if (judgeMatch(lat2) &&
                    judgeMatch(lat3) &&
                    latDown.element.type == lat2.element.type &&
                    latDown.element.type == lat3.element.type) {
                    return [lat.element, latDown.element];
                }
                //下横向判断左  -1 -2 lat  先确定col>1 
                if (col > 1) {
                    lat2 = lattices[i + Tool.colNum - 1];
                    lat3 = lattices[i + Tool.colNum - 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
                //下横向判断右  +1 +2 lat  先确定col<Tool.colNum -2
                if (col < Tool.colNum - 2) {
                    lat2 = lattices[i + Tool.colNum + 1];
                    lat3 = lattices[i + Tool.colNum + 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
                //下横向中间  +1 -1 lat  先确定col>0;<Tool.colNum -1
                if (col > 0 && col < Tool.colNum - 1) {
                    lat2 = lattices[i + Tool.colNum + 1];
                    lat3 = lattices[i + Tool.colNum - 1];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
                //
                //上横向判断左  -1 -2 latDown  先确定col>1 
                if (col > 1) {
                    lat2 = lattices[i - 1];
                    lat3 = lattices[i - 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latDown.element.type == lat2.element.type &&
                        latDown.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
                //上横向判断右  +1 +2 latDown  先确定col<Tool.colNum -2
                if (col < Tool.colNum - 2) {
                    lat2 = lattices[i + 1];
                    lat3 = lattices[i + 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latDown.element.type == lat2.element.type &&
                        latDown.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
                //下横向中间  +1 -1 latDown  先确定col>0;<Tool.colNum -1
                if (col > 0 && col < Tool.colNum - 1) {
                    lat2 = lattices[i + 1];
                    lat3 = lattices[i - 1];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latDown.element.type == lat2.element.type &&
                        latDown.element.type == lat3.element.type) {
                        return [lat.element, latDown.element];
                    }
                }
            }
            //与右交换,先确定col!=Tool.colNum-1,不是最右排元素8
            if (col != Tool.colNum - 1) {
                var latRight = lattices[i + 1];
                //能交换，并且类型不一致，并未判断latRight是否judgeMatch，也就是未判断是否魔力鸟，但是不影响
                if (this.judgeMove(latRight) && lat.element.type != latRight.element.type) {
                    //判断向右，+2，+3 lat 先确定col<Tool.colNum - 3
                    if (col < Tool.colNum - 3) {
                        lat2 = lattices[i + 2];
                        lat3 = lattices[i + 3];
                        if (judgeMatch(lat2) &&
                            judgeMatch(lat3) &&
                            lat.element.type == lat2.element.type &&
                            lat.element.type == lat3.element.type) {
                            return [lat.element, latRight.element];
                        }
                    }
                    //向左判断 -1 -2 latRight 先确定col>1
                    if (col > 1) {
                        lat2 = lattices[i - 1];
                        lat3 = lattices[i - 2];
                        if (judgeMatch(lat2) &&
                            judgeMatch(lat3) &&
                            latRight.element.type == lat2.element.type &&
                            latRight.element.type == lat3.element.type) {
                            return [lat.element, latRight.element];
                        }
                    }
                    //右纵向判断下  *1+1 *2+1 lat
                    lat2 = lattices[i + Tool.colNum * 1 + 1];
                    lat3 = lattices[i + Tool.colNum * 2 + 1];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                    //右纵向判断上  -*1+1 -*2+1 lat 
                    lat2 = lattices[i - Tool.colNum * 1 + 1];
                    lat3 = lattices[i - Tool.colNum * 2 + 1];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                    //右纵向中间  -*1 *1 lat  
                    lat2 = lattices[i + Tool.colNum + 1];
                    lat3 = lattices[i - Tool.colNum + 1];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        lat.element.type == lat2.element.type &&
                        lat.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                    //
                    //左纵向判断下  *1 *2 latRight 
                    lat2 = lattices[i + Tool.colNum * 1];
                    lat3 = lattices[i + Tool.colNum * 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latRight.element.type == lat2.element.type &&
                        latRight.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                    //左纵向判断上  -*1 -*2 latRight
                    lat2 = lattices[i - Tool.colNum * 1];
                    lat3 = lattices[i - Tool.colNum * 2];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latRight.element.type == lat2.element.type &&
                        latRight.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                    //左纵向中间  *1 -*1 latRight  
                    lat2 = lattices[i + Tool.colNum];
                    lat3 = lattices[i - Tool.colNum];
                    if (judgeMatch(lat2) &&
                        judgeMatch(lat3) &&
                        latRight.element.type == lat2.element.type &&
                        latRight.element.type == lat3.element.type) {
                        return [lat.element, latRight.element];
                    }
                }
            }
        }

        //检测特效的
        for (var i = 0; i < effectElements.length; i++) {
            var effectElement = effectElements[i];
            //是魔力鸟，直接找周围元素，
            if (effectElement.effectType == EffectType.MAGICLION) {
                //上格子
                var up = lattices[effectElement.index - Tool.colNum];
                if (this.judgeMove(up) && up.element.type != ElementType.LOLLIPOP) return [effectElement, up.element];
                //下格子
                var down = lattices[effectElement.index + Tool.colNum];
                if (this.judgeMove(down) && down.element.type != ElementType.LOLLIPOP) return [effectElement, down.element];
                //左格子
                var col = Tool.indexToRc(effectElement.index)[1];//列数
                if (col != 0) {
                    var left = lattices[effectElement.index - 1];
                    if (this.judgeMove(left) && left.element.type != ElementType.LOLLIPOP) return [effectElement, left.element];
                }
                if (col != Tool.colNum - 1) {
                    var right = lattices[effectElement.index + 1];
                    if (this.judgeMove(right) && right.element.type != ElementType.LOLLIPOP) return [effectElement, right.element];
                }
                continue
            }
            //是普通特效，找周围是特效的
            //上格子
            var up = lattices[effectElement.index - Tool.colNum];
            if (this.judgeMove(up) && up.element.effectType != null) return [effectElement, up.element];
            //下格子
            var down = lattices[effectElement.index + Tool.colNum];
            if (this.judgeMove(down) && down.element.effectType != null) return [effectElement, down.element];
            //左格子
            var col = Tool.indexToRc(effectElement.index)[1];//列数
            if (col != 0) {
                var left = lattices[effectElement.index - 1];
                if (this.judgeMove(left) && left.element.effectType != null) return [effectElement, left.element];
            }
            if (col != Tool.colNum) {
                var right = lattices[effectElement.index + 1];
                if (this.judgeMove(right) && right.element.effectType != null) return [effectElement, right.element];
            }
        }
        return null
    }

    /**
     * 判断已死
     * @param lattices 
     */
    public static alreadyDie(lattices: Lattice[]) {
        //记录基本元素的个数
        var arr = {};
        //记录是否有特效，两个任意特效，或一个魔力鸟特效，都未锁定
        var hasEffect = false;
        var effectCount = 0;
        //记录是否有两个格子挨着，且未锁定
        var hasTwo = false;
        //记录是否有三个格子挨着，且未锁定,没有毛球，这个逻辑略烦，暂定至少都未锁定，有bug再改
        var hasThree = false;
        for (var i = 0; i < lattices.length; i++) {
            if (this.judgeBaseEle(lattices[i]) &&
                !lattices[i].element.hasState(StateType.LOCK) &&
                !lattices[i].element.hasState(StateType.HAIRBALLGREY) &&
                !lattices[i].element.hasState(StateType.HAIRBALLBLACK) &&
                !lattices[i].element.hasState(StateType.HAIRBALLGREY)
            ) {
                var type = lattices[i].element.type;
                if (!arr[type]) arr[type] = 0;
                arr[type]++;

                var ele = lattices[i].element;

                if (ele.effectType == EffectType.MAGICLION) {
                    hasEffect = true;
                }
                if (ele.effectType != null) {
                    effectCount++;
                    if (effectCount >= 2) {
                        hasEffect = true;
                    }
                }
                var rc = Tool.indexToRc(i);
                //右1
                if (rc[1] < Tool.colNum - 1) {
                    var latRight1 = lattices[i + 1];
                    if (Tool.judgeMove(latRight1) && latRight1.element.type != ElementType.LOLLIPOP) {
                        hasTwo = true;
                    }
                    //右2
                    if (rc[1] < Tool.colNum - 2) {
                        var latRight2 = lattices[i + 2];
                        if (Tool.judgeMove(latRight1) &&
                            latRight1.element.type != ElementType.LOLLIPOP &&
                            Tool.judgeMove(latRight2) &&
                            latRight2.element.type != ElementType.LOLLIPOP) {
                            hasThree = true;
                        }
                    }
                }
                //下1
                var latDown1 = lattices[i + Tool.colNum];
                if (Tool.judgeMove(latDown1) && latDown1.element.type != ElementType.LOLLIPOP) {
                    hasTwo = true;
                }
                //提前判断下
                if (hasTwo && hasEffect) return false;

                //下2
                var latDown2 = lattices[i + Tool.colNum * 2];
                if (Tool.judgeMove(latDown1) &&
                    latDown1.element.type != ElementType.LOLLIPOP &&
                    Tool.judgeMove(latDown2) &&
                    latDown2.element.type != ElementType.LOLLIPOP) {
                    hasThree = true;
                }
                if (hasThree) {
                    for (var a in arr) {
                        if (arr[a] >= 3) {
                            return false
                        }
                    }
                }
            }
        }
        return true
    }

    /**
     * 将有相邻相同的组合,不是基础元素的，都会是单独的
     * @param arr 
     */
    public static fn(arr: number[], lattices: Lattice[]) {
        var result = [],
            i = 0;
        result[i] = [arr[0]];
        arr.reduce(function (prev, cur) {
            var latP = lattices[prev];
            var latC = lattices[cur];
            if (Tool.judgeMatch(latP) &&
                Tool.judgeMatch(latC) &&
                latP.element.type == latC.element.type) {
                result[i].push(cur)
            } else {
                result[++i] = [cur];
            }
            // cur.type == prev.type ? result[i].push(cur) : result[++i] = [cur];
            return cur;
        });
        return result;
    }

    /**
     * 返回元素类别
     * 元素初始化排列时，不能出现三消的情况
     * @param index 
     */
    public static returnType(index: number, lattices: Lattice[], baseElementTypes: number[]) {
        var rc = Tool.indexToRc(index);
        var arr = baseElementTypes.slice();
        //和左边两个比较
        if (rc[1] > 1) {
            //
            var lat1 = lattices[index - 1];
            var lat2 = lattices[index - 2];
            if (Tool.judgeMatch(lat1) &&
                Tool.judgeMatch(lat2) &&
                lat1.element.type == lat2.element.type) {
                Tool.removeEle(lat1.element.type, arr);
            }
        }
        //和上边两个比较
        var lat1 = lattices[index - Tool.colNum];
        var lat2 = lattices[index - Tool.colNum * 2];
        if (Tool.judgeMatch(lat1) &&
            Tool.judgeMatch(lat2) &&
            lat1.element.type == lat2.element.type) {
            Tool.removeEle(lat1.element.type, arr);
        }
        return Tool.randomT(arr);
    }

    /**
     * 根据关卡的地图数据generateLats
     * 
     * @param lattices
     * @param generateLats 
     * @return 数组，索引就是列数，每个元素是该列的生成口数据
     */
    public static setGenerateLats(lattices: Lattice[], generateLats: GenerateLatData[]): GenerateLatData[][] {
        var arr: GenerateLatData[][] = [];
        for (var i = 0; i < generateLats.length; i++) {
            var generateLat: GenerateLatData = generateLats[i];
            //防止为空
            generateLat.cus = generateLat.cus || [];
            var index = generateLat.index;
            if (!lattices[index]) continue
            lattices[index].isGenerate = true;
            var rc = this.indexToRc(index);
            //确实属于哪一列
            var col = rc[1];
            if (!arr[col]) arr[col] = [];
            arr[col].push({
                index: generateLat.index,
                type: generateLat.type || [],
                cus: generateLat.cus.slice(),//防止数据被修改
            });
        }
        //每一列的数据排个序,因为生成口掉落，index小的在前面
        for (var j = 0; j < arr.length; j++) {
            //排序从小到大
            if (arr[j]) arr[j].sort(function (a, b) { return a.index > b.index ? 1 : -1 });
        }
        return arr
        // var indexs = [];
        // for (var m = 0; m < Tool.colNum; m++) {
        //     //找每列第一个元素
        //     for (var g = 0; g < Tool.rowNum; g++) {
        //         var index = Tool.colNum * g + m;
        //         //如果格子存在，并且该列g的数据为1；
        //         if (lattices[index] && generateLats[m]) {
        //             lattices[index].isGenerate = true;
        //             indexs[m] = index;
        //             break
        //         }
        //     }
        // }
        // return indexs
    }
    /**
     * 将9*9都转化成01格子
     * @param nums 
     */
    public static setNumber01(nums: number[]): number[] {
        var arr: number[] = [];
        for (var i = 0; i < nums.length; i++) {
            if (nums[i]) {
                arr[i] = 1;
            } else {
                arr[i] = 0;
            }
        }
        return arr
    }
    /**
     * 找一个格子，最终掉落点，所有的index吧，不改变数据
     * @param lattice 能掉落的格子，格子上肯定有元素，并且能掉落
     * @param empty 
     * @param lattices 
     * @return 所有中间点吧数据
     */
    public static findBottom(lattice: Lattice, emptys: number[], lattices: Lattice[]): { index: number, type: FallType }[] {
        let indexDown: number, lat: Lattice;
        let connects: { index: number, type: FallType }[] = [];
        let isThrough: boolean = false;

        //找下面的
        if (lattice.down != null) {
            indexDown = lattice.down;
            isThrough = true;
        } else {
            indexDown = lattice.index + this.colNum;
        }
        let lastEmptyIndex: number;
        //下方有格子，并且再this.empty中
        let emptyIndex: number = emptys.indexOf(indexDown);

        //只要还在里面就一直找下,直到
        while (emptyIndex > -1) {
            if (isThrough) connects.push({ index: indexDown, type: FallType.THROUGH });

            //记录下上一个满足的
            lastEmptyIndex = emptyIndex;
            //下面格子变成lat
            lat = lattices[indexDown];
            if (lat.down != null) {
                //如果上次是false的,就结束加入
                if (!isThrough) connects.push({ index: indexDown, type: FallType.STRIGHT });
                isThrough = true
                indexDown = lat.down;
            } else {
                isThrough = false
                indexDown = lat.index + this.colNum;
            }
            emptyIndex = emptys.indexOf(indexDown);
        }
        if (lastEmptyIndex != undefined) {
            //说明已经加过，然后down无空格，直线已加
            if (isThrough) {
                return connects
            }
            //直线未加
            else {
                connects.push({ index: emptys[lastEmptyIndex], type: FallType.STRIGHT })
                return connects
            }
        } else {
            return null
        }
    }

    /**
     * 不改变数据
     * @param emptys 
     * @param lattices 
     */
    public static judgeOutOne(emptys: number[], lattices: Lattice[]) {
        for (var m = 0; m < Tool.colNum; m++) {
            //同一列
            for (var n = Tool.rowNum - 1; n >= 0; n--) {
                var index = Tool.rcToIndex(n, m);
                let lat = lattices[index];
                if (Tool.judgeFall(lat)) {
                    var indexs = Tool.findBottom(lat, emptys, lattices);
                    if (indexs != null) {
                        //超过一步，肯定true
                        if (indexs.length > 1) {
                            return true;
                        } else {
                            var downIndex: number = indexs[0].index;
                            var type: FallType = indexs[0].type;
                            //直线的
                            if (type == FallType.STRIGHT && (downIndex - index) / Tool.colNum > 1) {
                                return true;
                            }
                        }
                    }
                } else {
                    //如果有格子。无元素，是生成口,要补齐下面能掉落的
                    if (lat && !lat.element && lat.isGenerate) {
                        //找出从生成口一直到能掉落的空格
                        var arr = [];
                        var downIndex = index;
                        while (emptys.indexOf(downIndex) > -1) {
                            arr.push(downIndex);
                            if (lat.down != null) {
                                downIndex = lat.down;
                            } else {
                                downIndex = lat.index + this.colNum;
                            }
                            lat = lattices[downIndex];
                        }
                        if (arr.length > 1) return true
                    }
                }
            }
        }
        return false;
    }
    /**
     * 十位是基础元素类型，对应数字减1就是基础元素类型
     * 个位是特效类型（0表示无特效），对应数字减1就是特效类型
     * @param num 
     * @return [] 0下标是元素类型（返回null说明不合规则），1下标是特效类型
     */
    public static praseEleNumber(num) {
        //解析num;
        var arr: number[] = this.returnTO(num);
        //基础类型，没有就是null，有就-1，对ElementType对应
        var baseType = arr[1] ? arr[1] - 1 : null;
        //不是基础类型
        if (FiveBaseElementTypes.indexOf(baseType) == -1) baseType = null;
        //特效类型
        var effectType = arr[0] ? arr[0] - 1 : null;
        return [baseType, effectType];
    }

    /**
     * 从数组移除一个元素
     * @param e 
     * @param arr 
     */
    public static removeEle(e, arr) {
        var index = arr.indexOf(e);
        if (index >= 0) {
            arr.splice(index, 1)
        }
    }
    /**
     * 
     * @param e 
     * @param n 
     */
    public static randomT(e, n?) {
        return e && "number" == typeof e.length && e.length ? e[Math.floor(Math.random() * e.length)] : ("number" != typeof n && (n = e || 1, e = 0), e + Math.random() * (n - e))
    }

    /**
     * 数字数组去重
     */
    public static removeReapty(arr: number[]) {
        var obj = {};
        for (var i = arr.length - 1; i >= 0; i--) {
            if (obj[arr[i]]) {
                //有出现的的，去掉
                arr.splice(i, 1)
            } else {
                obj[arr[i]] = true
            }
        }
    }
    /**
     * 获得元素
     * @param type 
     */
    public static getElement(type: ElementType): Element {
        let obj: Element = Pool.takeOut(RecoverName.ELEMENT);
        if (!obj) {
            obj = new Element(type)
        } else {
            obj.reset(type)
        }
        return obj
    }
    /**
     * 获得矩形遮罩
     */
    public static getRectMask(): RectMask {
        let rect: RectMask = Pool.takeOut(RecoverName.RECT_MASK);
        if (!rect) rect = new RectMask()
        return rect;
    }

    /**
     * 返回个十百等，0是个位 ,1是十位，依次
     * 78[ 8, 7]
     * 789[ 9, 8, 7]
     * @param num 整数
     */
    public static returnTO(num: number): number[] {
        var length = num.toString().length;
        var arr = [];
        var a: any;
        for (var i = 0; i < length; i++) {
            a = (num % Math.pow(10, i + 1)) / Math.pow(10, i);
            arr.push(parseInt(a))
        }
        return arr
    }

    //算角度，弧度，x正方向为0，0到2pi，顺时针为正
    public static getForwardRotation(fromPoint: number[], toPoint: number[]): number {
        var deltaX = toPoint[0] - fromPoint[0];
        var deltaY = toPoint[1] - fromPoint[1];
        var vec1 = [1, 0];
        var vec2 = [deltaX, deltaY];
        var a = Math.sqrt(vec1[0] * vec1[0] + vec1[1] * vec1[1])
        var b = Math.sqrt(vec2[0] * vec2[0] + vec2[1] * vec2[1])
        var ab = vec1[0] * vec2[0] + vec1[1] * vec2[1]
        var cos = ab / (a * b);//反余弦函数范围0到pi
        var r = Math.acos(cos);
        if (deltaY < 0) {
            r = Math.PI * 2 - r;
        }
        return r
    }
    /**
     * 随机取数组arr中count个元素
     * @param arr 
     * @param count 
     */
    public static getRandomArrayElements(arr: any[], count: number) {
        var shuffled = arr.slice(0), i = arr.length, min = i - count, temp, index;
        //如果count大于等于数组长度，返回所有数组
        if (min <= 0) return shuffled;
        //随机排序，然后取出后面的元素
        while (i-- > min) {
            index = Math.floor((i + 1) * Math.random());
            temp = shuffled[index];
            shuffled[index] = shuffled[i];
            shuffled[i] = temp;
        }
        return shuffled.slice(min);
    }
    public static getRandomArrayElementsEx(arr: any[], count: number): any[] {
        //如果count大于等于数组长度，返回所有数组
        if (arr.length <= count) return arr.slice();
        if (count <= 0) return [];
        var arrCopy = arr.slice();
        var outArr = [];
        while (count--) {
            var rand = Math.floor(Math.random() * arrCopy.length);
            var ele = arrCopy.splice(rand, 1)[0];
            outArr.push(ele);
        }
        return outArr
    }
}