/*
 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const { XTools } = require('../engine/XTools');
const { X2DFast } = require('../engine/graphics/X2DFast');
const { NapiLog } = require('./NapiLog');

class LogParser {
  constructor(result) {
    const regexVT100 = /\x1B\[[0-9;]*[A-Za-z]/g; // 匹配VT100字符的正则表达式
    result = result.replace(regexVT100, '');
    result = result.replace(/\t/g, '    ');
    result = result.replace(/\r/g, '');
    this.logLines_ = result.split('\n');
    this.stat_ = 0;
    this.lineNum_ = 0;
    this.initBlock_ = {
      filePoint: null,
      funcPoint: null,
      blockType: -1,
    };
    this.procBlock_ = {
      blockStat: -1,
      blockCollect: -1,
      oneBlock: -1,
    };
    this.procNormal_ = null;
    this.output_ = {};
  }

  parsing() {
    if (this.lineNum_ >= this.logLines_.length) {
      return false;
    }

    for (let i = 0; i < 300 && this.lineNum_ < this.logLines_.length; i++) {
      this.parseLine(this.logLines_[this.lineNum_]);
      this.lineNum_++;
    }

    XTools.PROC_TO = this.lineNum_ * 20 / this.logLines_.length;
    return true;
  }
  isBlock(l) {
    for (let bt of XTools.CONFIG.BlockTypes) {
      if (l.indexOf(bt) >= 0) {
        this.stat_ = this.initBlock_.blockType;
        this.procBlock_.blockStat = 0;
        this.procBlock_.blockCollect = {
          type: 'block:' + bt,
          func: this.initBlock_.funcPoint,
          file: this.initBlock_.filePoint,
          irList: [],
          startLine: l,
        };
        this.procBlock_.oneBlock = null;
        return true;
      }
    }
    return false;
  }
  splitLast(s) {
    let i = s.lastIndexOf('@');

    return [s.substring(0, i), s.substring(i + 1)];
  }
  isStart(l) {
    //========= After bytecode2circuit lowering [func_main_0@484@arkcompiler/ets_runtime/sd_test/ttabs.abc] ========
    const regexStart = /=+ *After ([a-zA-Z0-9_ ]+) \[([#a-zA-Z0-9_@/.-]+)\] *=+/g;
    //========= After inlining [OthreMath@test@arkcompiler/ets_runtime/sd_test/test.abc] Caller
    //method [func_main_0@641@arkcompiler/ets_runtime/sd_test/test.abc]====================
    const regexStart2 = /=+ *After ([a-zA-Z0-9_ ]+) \[([a-zA-Z0-9_@/.-]+)\] *Caller method \[([#a-zA-Z0-9_@/.]+)\] *=+/g;

    if (l[11] !== '=') {
      return;
    }
    let ret = regexStart.exec(l);
    if (ret) {
      let tt = this.splitLast(ret[2]);
      this.procNormal_ = {
        type: ret[1], //优化类型
        func: tt[0], //函数名
        file: tt[1], //文件名
        irList: [],
        startLine: l,
      };
      this.stat_ = 1;
      [this.initBlock_.funcPoint, this.initBlock_.filePoint] = [tt[0], tt[tt.length - 1]];
      this.initBlock_.blockType = 10;
    }
    else {
      ret = regexStart2.exec(l);
      if (ret) {
        let tt = this.splitLast(ret[2]);
        let tt2 = this.splitLast(ret[3]);
        NapiLog.logInfo(tt[0], 'Caller method(' + this.lineNum_ + '行)', ret[3]);
        this.procNormal_ = {
          type: ret[1] + ' ' + tt[0], //优化类型
          func: tt2[0], //函数名
          file: tt2[1], //文件名
          irList: [],
          startLine: l,
        };
        this.stat_ = 1;
        [this.initBlock_.funcPoint, this.initBlock_.filePoint] = [tt2[0], tt2[tt2.length - 1]];
        this.initBlock_.blockType = 10;
      }
      else {
        if (l.search('After') > 0) {
          alert(l);
        }
      }
    }
  }
  collectNormal(l) {
    let idx = l.search('{"id":');
    if (idx >= 0) {
      let idx2 = l.lastIndexOf('}');
      let str = l.substring(idx, idx2 + 1);

      let ir = JSON.parse(str);
      { //根据XTools.CONFIG.MTypeField切割MType
        let cutPos = [];
        for (let field of XTools.CONFIG.MTypeField) {
          let idx = ir.MType.indexOf(', ' + field);
          if (idx >= 0) {
            cutPos.push(idx);
          }
        }
        cutPos.push(ir.MType.length);
        cutPos.sort((a, b) => { return parseInt(a) - parseInt(b) });
        if (cutPos[0] === 0) {
          cutPos.shift();
        }
        let p = 0;
        let cutResult = [];
        for (let i of cutPos) {
          let tmp = ir.MType.substring(p, i);
          if (tmp.startsWith(', ')) {
            tmp = tmp.substring(2);
          }
          if (tmp.endsWith(', ')) {
            tmp = tmp.substring(0, tmp.length - 2);
          }
          cutResult.push(tmp);
          p = i;
        }
        cutResult.push('inNum=[' + ir.in[0].length + ',' + ir.in[1].length + ',' + ir.in[2].length + ',' + ir.in[3].length + ',' + ir.in[4].length + ']');
        cutResult.push('outNum=' + ir.out.length);
        if ('comment' in ir) {
          cutResult.push('//' + ir.comment);
        }
        ir.maxDetailWidth = 0;
        for (let detail of cutResult) {
          let w = X2DFast.gi().getTextWidth(detail, 14);
          if (ir.maxDetailWidth < w) {
            ir.maxDetailWidth = w;
          }
        }
        ir.detailList = cutResult;
      }
      this.procNormal_.irList.push(ir);
    }
    else {
      //= End typeHitRate: 0.500000 =
      let regexEnd = /=+ End[a-zA-Z:.0-9 ]* =+/g;
      let tt = regexEnd.exec(l);
      if (tt) { //收集结束,入大表l.search('== End ==') > 0
        if (this.procNormal_.irList.length > 0) {
          if (!(this.procNormal_.file in this.output_)) {
            this.output_[this.procNormal_.file] = {};
          }
          if (!(this.procNormal_.func in this.output_[this.procNormal_.file])) {
            this.output_[this.procNormal_.file][this.procNormal_.func] = [];
          }
          this.output_[this.procNormal_.file][this.procNormal_.func].push(this.procNormal_);
        }
        else {
          NapiLog.logError('After和End之间没有指令(' + this.lineNum_ + '行)');
        }
        this.stat_ = 0;
      }
      else {
        NapiLog.logError('After和End之间解析失败(' + (this.lineNum_ + 1) + ')行');
        this.stat_ = 0;
      }
    }
  }
  parseLine(l) {
    switch (this.stat_) {
      case 0: //搜索起始
        if (this.SearchBegin(l) || this.isBlock(l)) {
          return;
        }
        this.isStart(l);
        break;
      case 1: //收集ir表
        this.collectNormal(l);
        break;
      case 10: //收集block一
        if (this.CollectBlock(l)) {
          this.stat_ = 0;
          this.lineNum_ -= 1;
        }
        break;
      case 20: //收集block二
        if (this.CollectBlock2(l)) {
          this.stat_ = 0;
          this.lineNum_ -= 1;
        }
        break;
    }
  }

  static Load(fn, cb) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', fn);
    xhr.setRequestHeader('Cache-Control', 'no-cache');
    xhr.onreadystatechange = () => {
      if (xhr.readyState === XMLHttpRequest.DONE) {
        if (xhr.status === 200) {
          XTools.PORC_TO = 10;
          cb(fn, xhr.responseText);
        }
      }
    };
    xhr.send();
  }
  NumberStringToArray(ss) {
    let outs = ss.split(',');
    let ret = [];
    for (let s of outs) {
      let ttt = parseInt(s);
      if (!isNaN(ttt)) {
        ret.push(ttt);
      }
    }
    return ret;
  }
  SearchBegin(l) {
    let ret;
    let ib = this.initBlock_;

    if (l.startsWith('[compiler] aot method')) {
      //[compiler] aot method [func_main_0@b.abc] log:
      const regexFuncName = /^\[compiler\] aot method \[([#a-zA-Z0-9_@/.]+)\] (recordName \[[a-zA-Z0-9_]*\] )*log:/g;
      ret = regexFuncName.exec(l);
      if (ret) {
        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
        ib.blockType = 10;
        return true;
      }
    }
    if (l.startsWith('[compiler] ==================== Before state split')) {
      const regexFuncName2 = /^\[compiler\] =+ Before state split linearizer \[([#a-zA-Z0-9_@/.]+)\] *=*/g;
      ret = regexFuncName2.exec(l);
      if (ret) {
        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
        ib.blockType = 20;
        return true;
      }
    }
    if (l.startsWith('[compiler] ==================== After graph lineari')) {
      const regexFuncName3 = /^\[compiler\] =+ After graph linearizer \[([#a-zA-Z0-9_@/.]+)\] *=*/g;
      ret = regexFuncName3.exec(l);
      if (ret) {
        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
        ib.blockType = 20;
        return true;
      }
    }
    return false;
  }
  CollectBlock(l) {
    const regexBlock = [
      /^\[compiler\] B([0-9]+):                               ;preds=([0-9, ]*)$/g, //[compiler] B0:                               ;preds=
      /^\[compiler\]  *Succes:([0-9, ]*)$/g, //[compiler] 	Succes:
      /^\[compiler\]  *Bytecode\[\] = *(Empty)*$/g, //[compiler] 	Bytecode[] = Empty
      /^\[compiler\]  *Trys:([0-9, ]*)$/g, //[compiler] 	Trys:
    ];
    let ret;
    let pb = this.procBlock_;
    if (pb.blockStat === 0) {
      ret = regexBlock[0].exec(l);
      if (ret) {
        pb.oneBlock = {
          id: ret[1],
          op: 'B' + ret[1],
          detailList: [],
          maxDetailWidth: 0,
        };
        pb.oneBlock.in = [[], [], [], [], this.NumberStringToArray(ret[2])];
        return false;
      }
      if (!pb.oneBlock) { //完成了一个block的解析
        if (!(pb.blockCollect.file in this.output_)) {
          this.output_[pb.blockCollect.file] = {};
        }
        if (!(pb.blockCollect.func in this.output_[pb.blockCollect.file])) {
          this.output_[pb.blockCollect.file][pb.blockCollect.func] = [];
        }
        this.output_[pb.blockCollect.file][pb.blockCollect.func].push(pb.blockCollect);
        return true;
      }
      ret = regexBlock[1].exec(l);
      if (ret) {
        pb.oneBlock.out = this.NumberStringToArray(ret[1]);
        return false;
      }
      ret = regexBlock[2].exec(l);
      if (ret) {
        pb.blockStat = 1;
        return false;
      }
    }
    else if (pb.blockStat === 1) { //开始记录bytecode,直到空行,结束这个block
      if (/^\[compiler\] *$/g.test(l)) { //遇到空行,完成block
        if (pb.oneBlock.maxDetailWidth === 0) {
          pb.oneBlock.maxDetailWidth = X2DFast.gi().getTextWidth('Empty', 14);
          pb.oneBlock.detailList.push('Empty');
        }
        pb.blockCollect.irList.push(pb.oneBlock);
        pb.oneBlock = null;
        pb.blockStat = 0;
      }
      else {
        let s = l.substring(11);
        while (s.startsWith(' ')) {
          s = s.substring(1);
        }
        let w = X2DFast.gi().getTextWidth(s, 14);
        if (pb.oneBlock.maxDetailWidth < w) {
          pb.oneBlock.maxDetailWidth = w;
        }
        pb.oneBlock.detailList.push(s);
      }
    }
    return false;
  }
  CollectBlock2(l) {
    const regexBlock = [
      /^\[compiler\] B([0-9]+):/g, //[compiler] B0:                               ;preds=
      /^\[compiler\]  *Preds:([0-9, ]*)$/g,
      /^\[compiler\]  *Succes:([0-9, ]*)$/g, //[compiler] 	Succes:
      /^\[compiler\]  *Bytecode\[\] = *(Empty)*$/g, //[compiler] 	Bytecode[] = Empty
      /^\[compiler\]  *Trys:([0-9, ]*)$/g, //[compiler] 	Trys:
    ];
    let pb = this.procBlock_;
    let ret;
    switch (pb.blockStat) {
      case 0:
        ret = regexBlock[0].exec(l);
        if (ret) {
          pb.oneBlock = {
            id: ret[1],
            op: 'B' + ret[1],
            detailList: [],
            maxDetailWidth: 0,
          };
          pb.blockStat = 1;
          return false;
        }
        if (!pb.oneBlock) { //完成了一个block的解析
          if (!(pb.blockCollect.file in this.output_)) {
            this.output_[pb.blockCollect.file] = {};
          }
          if (!(pb.blockCollect.func in this.output_[pb.blockCollect.file])) {
            this.output_[pb.blockCollect.file][pb.blockCollect.func] = [];
          }
          this.output_[pb.blockCollect.file][pb.blockCollect.func].push(pb.blockCollect);
          return true;
        }
        break;
      case 1:
        ret = regexBlock[1].exec(l);
        if (ret) {
          pb.oneBlock.in = [[], [], [], [], this.NumberStringToArray(ret[1])];
          pb.blockStat = 2;
          return false;
        }
        break;
      case 2:
        ret = regexBlock[2].exec(l);
        if (ret) {
          pb.oneBlock.out = this.NumberStringToArray(ret[1]);
          pb.blockStat = 3;
          return false;
        }
        break;
      case 3: //开始记录bytecode,直到空行,结束这个block
        if (/^\[compiler\] *$/g.test(l)) { //遇到空行,完成block
          if (pb.oneBlock.maxDetailWidth === 0) {
            pb.oneBlock.maxDetailWidth = X2DFast.gi().getTextWidth('Empty', 14);
            pb.oneBlock.detailList.push('Empty');
          }
          pb.blockCollect.irList.push(pb.oneBlock);
          pb.oneBlock = null;
          pb.blockStat = 0;
        }
        else {
          let s = l.substring(11);
          while (s.startsWith(' ')) {
            s = s.substring(1);
          }
          let w = X2DFast.gi().getTextWidth(s, 14);
          if (pb.oneBlock.maxDetailWidth < w) {
            pb.oneBlock.maxDetailWidth = w;
          }
          pb.oneBlock.detailList.push(s);
        }
        return false;
      default:
        return false;
    }
    return false;
  }
}

module.exports = {
  LogParser
};