import {Parser, RecognitionException, Token} from "antlr4ts";
import {ErrorMark} from "../api/vo/errorMark";
import {Recovermark} from "./recovermark";
import {Interval, IntervalSet} from "antlr4ts/misc";

/**
 * 策略相关的工具，公共方法抽取
 */
export class StrategyUtil {

    /**
     * 根据修复中的异常信息，产生一个错误提示的消息
     * @param e
     */
    public static generateErrorMark(e: RecognitionException) {
        /**
         * 修复状态下的解析不处理错误提示
         */
        if (Recovermark.isRecover){
            return;
        }

        let errorToken: Token = e.getOffendingToken();
        let errorMark = new ErrorMark();
        errorMark.lineOffset = errorToken.line;
        errorMark.startOffset = errorToken.startIndex;
        errorMark.endOffset = errorToken.stopIndex;
        errorMark.message = "There may be a syntax error, please check";
        if(!StrategyUtil.isExistInErrorMarkList(errorMark)){
            Recovermark.errorMarkList.push(errorMark);
        }

    }

    private static isExistInErrorMarkList(errorMark: ErrorMark): boolean {
        debugger
        for (let mark of Recovermark.errorMarkList){
            if (errorMark.startOffset == mark.startOffset && errorMark.endOffset == mark.endOffset){
                return true;
            }

        }

        return false;

    }


    /**
     * 修复动作
     * @param recognizer
     * @param e
     */
    public static repair(recognizer: Parser, e: RecognitionException) {
        //1.定位具体出错的Token
        let errorToken: Token = e.getOffendingToken();

        let expectTokenText:string = null;
        let minDistanceNumber:number = -1;


        //2.找到FollowSet集合(去掉注释，结束符等等)
        let intervalSet:IntervalSet = e.expectedTokens;
        let intervals:Interval[] = intervalSet.intervals;

        let expectTokenTextArray: Array<string> = new Array<string>();

        for (let interval of intervals){
            let start:number = interval.a;
            let end:number = interval.b;
            for (let i = start; i < end; i++) {
                let text:string = StrategyUtil.getTokenTextByType(recognizer,i);
                if (text == "EOF"){
                    continue;
                }
                expectTokenTextArray.push(text)
            }
        }

        //3.字符串匹配算法，计算正确的Token字符串
        for (let i in expectTokenTextArray){
            let str = expectTokenTextArray[i];
            let distance:number = StrategyUtil.minDistance(errorToken.text, str);
            if (minDistanceNumber == -1){
                minDistanceNumber = distance;
                expectTokenText = str;
                continue;
            }
            if (distance < minDistanceNumber){
                minDistanceNumber = distance;
                expectTokenText = str;
            }
        }


        //4.在TokenStreamRewrite上进行修改。
        //保存光标所在位置
        let rangeOffset: number = Recovermark.editorContext.rangeOffset;
        let offSetInToken: Token = undefined;
        Recovermark.rewriteStream.getText();
        Recovermark.rewriteStream.getText();
        let tokenSize = Recovermark.rewriteStream.getTokenStream().size;
        for (let i = tokenSize - 1; i > 0; i--) {
            let currentToken:Token = Recovermark.rewriteStream.getTokenStream().get(i);
            if (currentToken.startIndex <= rangeOffset){
                offSetInToken = currentToken;
            }
        }


        Recovermark.rewriteStream.replace(errorToken.tokenIndex,errorToken.tokenIndex, expectTokenText);


        //5.重新新生一个新的请求对象。
        if (offSetInToken.tokenIndex == errorToken.tokenIndex){
            rangeOffset = errorToken.startIndex;
        }

        if (offSetInToken.tokenIndex > errorToken.tokenIndex){
            rangeOffset = errorToken.startIndex;
        }
    }


    /**
     * 根据token的type转换成token的text
     * @param recognizer
     * @param typeNumber
     */
    private static getTokenTextByType(recognizer: Parser,typeNumber: number):string | undefined {
        let tokenTypeMap:ReadonlyMap<string, number> = recognizer.getTokenTypeMap();
        let tokenText:string = undefined;
        tokenTypeMap.forEach((value, key, map) => {
            if (value == typeNumber){
                return tokenText = key;
            }
        });

        return tokenText;


    }


    private static minDistance(str1, str2):number{

        //计算两个字符串的长度。
        var len1 = str1.length;
        var len2 = str2.length;
        //建立上面说的数组，比字符长度大一个空间
        var dif = [];

        //赋初值，步骤B。
        for (var a = 0; a <= len1; a++) {
            dif[a] = [];
            dif[a][0] = a;
        }
        for (var a = 0; a <= len2; a++) {
            dif[0][a] = a;
        }
        //计算两个字符是否一样，计算左上的值
        var temp;
        for (var i = 1; i <= len1; i++) {
            for (var j = 1; j <= len2; j++) {
                if (str1[i - 1] == str2[j - 1]) {
                    temp = 0;
                } else {
                    temp = 1;
                }
                //取三个值中最小的
                dif[i][j] = StrategyUtil.min([
                    dif[i - 1][j - 1] + temp,
                    dif[i][j - 1] + 1,
                    dif[i - 1][j] + 1
                ]);
            }
        }
        //System.out.println("字符串\""+str1+"\"与\""+str2+"\"的比较");
        //取数组右下角的值，同样不同位置代表不同字符串的比较
        console.log("差异步骤：" + dif[len1][len2]);

        //计算相似度
        var similarity = 1 - dif[len1][len2] / Math.max(str1.length, str2.length);
        //System.out.println("相似度："+similarity);
        return similarity;

    }

    //得到最小值
    private static min(ints) {
        var min = 0;
        for (var i = 0; i < ints.length; i++) {
            if (min > ints[i]) {
                min = ints[i];
            }
        }
        return min;
    }



}