import { LanguageService } from "./../services/languageService";
import { getInstance as LocalizedStrings } from "./../services/localizedStringsService";
import { Operators } from "./../ast/operators";


/**
 *
 * source: https://stackoverflow.com/a/16270434
 */
export function isElementInViewport (el) {
  const rect = el.getBoundingClientRect();

  return (
    rect.bottom > 0 &&
    rect.right > 0 &&
    rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
    rect.top < (window.innerHeight || document.documentElement.clientHeight)
  );
}

let cacheMainList = null;
let cacheOp = null;

function fillCache () {
  if (cacheMainList == null) {
    cacheMainList = [];
    const mainList = [
      "RK_PROGRAM",
      "RK_REAL",
      "RK_VOID",
      "RK_BOOLEAN",
      "RK_STRING",
      "RK_INTEGER",
      "RK_CHARACTER",
      "RK_SWITCH",
      "RK_CASE",
      "RK_DEFAULT",
      "RK_CONST",
      "RK_FUNCTION",
      "RK_RETURN",
      "RK_FOR",
      "RK_BREAK",
      "RK_DO",
      "RK_WHILE",
      "RK_IF",
      "RK_ELSE",
      "RK_FALSE",
      "RK_TRUE",
    ];
    const lexer = LanguageService.getCurrentLexer();
    const names = lexer.getReservedKeys();
    for (let key = 0; key < mainList.length; ++key) {
      const word = mainList[key];
      const keyword = names[word];
      cacheMainList.push(keyword);
    }
  }
  if (cacheOp == null) {
    cacheOp = [];
    const logicOpList = [
      Operators.AND.value,
      Operators.OR.value,
      Operators.NOT.value,
    ];
    for (let op = 0; op < logicOpList.length; ++op) {
      const lOp = `logic_operator_${logicOpList[op]}`;
      cacheOp.push(LocalizedStrings().getUI(lOp));
    }
  }
}

export function isKeyword (text) {
  fillCache();
  for (let key = 0; key < cacheMainList.length; ++key) {
    const keyword = cacheMainList[key];
    if (keyword == text) {
      return true;
    }
  }
  // not in main list, check op
  for (let op = 0; op < cacheOp.length; op++) {
    const lOp = cacheOp[op];
    if (lOp == text) {
      return true;
    }
  }
  return false;
}

export function isValidIdentifier (identifier_str) {
  const validRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier_str);
  return validRegex && !isKeyword(identifier_str)
}

export function getCodeEditorModeConfig () {
  const blockList = [
    "RK_SWITCH",
    "RK_PROGRAM",
    "RK_CASE",
    "RK_DEFAULT",
    "RK_FOR",
    "RK_FOR_ALT",
    "RK_FUNCTION",
    "RK_DO",
    "RK_WHILE",
    "RK_WHILE_ALT",
    "RK_IF",
    "RK_ELSE",
  ];
  const keywordsList = [
    "RK_CONST",
    "RK_RETURN",
    "RK_BREAK",
    "RK_FOR_FROM",
    "RK_FOR_TO",
    "RK_FOR_PASS",
    "RK_DO_UNTIL",
  ];
  const typeList = [
    "RK_REAL",
    "RK_VOID",
    "RK_BOOLEAN",
    "RK_STRING",
    "RK_INTEGER",
    "RK_CHARACTER",
  ];
  const atomList = ["RK_FALSE", "RK_TRUE"];

  const case_default = [];
  const blocks = [];
  const keywords = [];
  const types = [];
  const atoms = [];
  let switchString = "";

  cacheMainList = [];
  const lexer = LanguageService.getCurrentLexer();
  const names = lexer.getReservedKeys();
  //console.debug(names);
  blockList.forEach((v) => {
    const keyword = names[v];
    const value = keyword;
    cacheMainList.push(value);
    keywords.push(value);
    blocks.push(value);
    if (v == "RK_SWITCH") {
      switchString = value;
    } else if (v == "RK_CASE" || v == "RK_DEFAULT") {
      case_default.push(value);
    }
  });
  keywordsList.forEach((v) => {
    const keyword = names[v];
    const value = keyword;
    cacheMainList.push(value);
    keywords.push(value);
  });
  typeList.forEach((v) => {
    const keyword = names[v];
    const value = keyword;
    cacheMainList.push(value);
    types.push(value);
  });
  atomList.forEach((v) => {
    const keyword = names[v];
    const value = keyword;
    cacheMainList.push(value);
    atoms.push(value);
  });

  cacheOp = [];
  const logicOpList = ["NOT_OPERATOR", "OR_OPERATOR", "AND_OPERATOR"];

  logicOpList.forEach((v) => {
    const keyword = names[v];
    const value = keyword;
    cacheOp.push(value);
    keywords.push(value);
  });
  cacheMainList.push(lexer.getLangFuncs().main_function);
  return {
    case_default: case_default,
    atoms: atoms,
    keywords: keywords,
    switchString: switchString,
    types: types,
    blocks: blocks,
  };
}

/**
 * Source: https://gist.github.com/andrei-m/982927
 * @param {string} a
 * @param {string} b
 */
export function levenshteinDistance (a, b) {
  if (a.length == 0) return b.length;
  if (b.length == 0) return a.length;

  const matrix = [];

  // increment along the first column of each row
  let i;
  for (i = 0; i <= b.length; i++) {
    matrix[i] = [i];
  }

  // increment each column in the first row
  let j;
  for (j = 0; j <= a.length; j++) {
    matrix[0][j] = j;
  }

  // Fill in the rest of the matrix
  for (i = 1; i <= b.length; i++) {
    for (j = 1; j <= a.length; j++) {
      if (b.charCodeAt(i - 1) == a.charCodeAt(j - 1)) {
        matrix[i][j] = matrix[i - 1][j - 1];
      } else {
        matrix[i][j] = Math.min(
          matrix[i - 1][j - 1] + 1, // substitution
          Math.min(
            matrix[i][j - 1] + 1, // insertion
            matrix[i - 1][j] + 1
          )
        ); // deletion
      }
    }
  }
  return matrix[b.length][a.length];
}

let win = null;
export function openAssessmentDetail (event) {
  event.preventDefault();
  const page_code = event.currentTarget.dataset.page;
  if (win != null) {
    win.close();
  }
  win = window.open("", "DetailWindow", "width=550,height=600");
  win.document.open();
  win.document.write(page_code);
  win.document.close();
}

export function range (size, startAt = 0) {
  return [...Array(size).keys()].map((i) => i + startAt);
}

/**
 *
 * @param {number} ms
 */
export async function sleep (ms) {
  return new Promise((res, _) => setTimeout(res, ms));
}
