import { HZEngineCore, UI } from "../../index.js";

export function menu_statement(core: HZEngineCore) {
  // menu statement start
  core.script.use((ctx, next) => {
    if (ctx.rawtext.trim().split(" ")[0] !== "menu") return next();
    let menu_data = ctx.startStatement("menu") as unknown as MenuStatementData;

    // Block the game
    core.system.block();

    let router = core.ui.getRouter("menu");

    if (!router) {
      core.ui.addRouter("menu", "ct");
      router = core.ui.getRouter("menu")!;
    }

    let menu_view_prop = buildMenuViewProp(menu_data);
    if (!router.length) {
      router.push("menu", menu_view_prop);
    } else {
      router.update(menu_view_prop);
    }
    // the view contains many buttons, when the button is clicked,
    // it will call core.script.jump() to jump to the next line of the @label statement
    // then hide the menu
    // so the script will continue to execute the next line of the @label statement
    // when a @label is appended again, it represents the command between the @label and the next @label
    // is executed completely, so jump to the next line of "end menu"
  });

  // menu statement close
  core.script.use((ctx, next) => {
    if (ctx.rawtext.trim().split(/ +/).join(" ") !== "end menu") return next();

    ctx.endStatement("menu");
  });

  // menu internal label (start with @)
  // syntax: @"{label_name}" [enable={js_expression}]
  core.script.use((ctx, next) => {
    if (!ctx.rawtext.trim().split(" ")[0].startsWith("@")) return next();
    let label_name = ctx.slicedArgs[1].str;
    if (
      ctx.statementStack.length === 0 ||
      ctx.statementStack[ctx.statementStack.length - 1][0] !== "menu"
    ) {
      throw `@${label_name} must in menu statement`;
    }
    let menu_item = ctx.statementStack[ctx.statementStack.length - 1];

    let menu_data = menu_item[2] as unknown as MenuStatementData;

    for (let i = 0; i < menu_data.item_list.length; i++) {
      if (
        menu_data.item_list[i].position[0] === ctx.currentPath &&
        menu_data.item_list[i].position[1] === ctx.currentLineIndex
      ) {
        // the label belongs to this menu statement
        // so jump to the "end menu" statement
        // the script will continue from the "end menu" statement

        // get the end menu statement position
        let end_menu_position: [path: string, index: number] = [
          ...menu_data.end_position,
        ];

        // jump to "end menu"
        core.script.jump(end_menu_position[0], end_menu_position[1]);
        return;
      }
    }

    // the label does not belong to this menu statement, so throw error
    throw `@${label_name} does not belong to closest menu statement, at file [${
      ctx.currentPath
    }] line [${ctx.currentLineIndex + 1}], but belong to [${
      menu_item[1][0]
    }] line [${menu_item[1][1] + 1}]`;
  });

  // analyse menu start statement
  core.script.useAnalyseStatement((ctx, next) => {
    if (ctx.rawtext.trim().split(" ")[0] !== "menu") return next();
    let data = ctx.startStatement("menu");

    // console.log("[HZEngine] Menu start");

    // the data here is {} at first, so dont read its properties, because the properties are not defined yet
    // we will define the properties here
    (data as unknown as MenuStatementData).start_position = [
      ctx.currentPath,
      ctx.currentLineIndex,
    ];
    (data as unknown as MenuStatementData).item_list = [];
  });

  // analyse menu label (start with @)
  core.script.useAnalyseStatement((ctx, next) => {
    if (!ctx.rawtext.trim().split(" ")[0].startsWith("@")) return next();
    if (
      ctx.statementStack.length === 0 ||
      ctx.statementStack[ctx.statementStack.length - 1][0] !== "menu"
    ) {
      throw `@${ctx.slicedArgs[1]} must in menu statement`;
    }

    // console.log("[HZEngine] Menu label: " + ctx.slicedArgs[1].str);

    let menu_stack_item = ctx.statementStack[ctx.statementStack.length - 1];

    // parse the enable js expression (syntax: enable={js_expression})
    // get the js_expression and put it in the menu data
    let enable_js_expression: string | undefined = undefined;
    if (ctx.slicedArgs.length > 2) {
      enable_js_expression = ctx.slicedArgs[2].str!;

      if (!enable_js_expression.startsWith("enable=")) {
        throw `enable js expression must start with "enable="`;
      }
      enable_js_expression = enable_js_expression.slice(7);
      if (
        enable_js_expression.startsWith("{") &&
        enable_js_expression.endsWith("}")
      ) {
        enable_js_expression = enable_js_expression.slice(1, -1);
      } else {
        enable_js_expression = enable_js_expression.trim();
      }

      if (enable_js_expression.length > 0) {
        enable_js_expression = `(${enable_js_expression})`;
      } else {
        throw `enable js expression must not be empty`;
      }
    }

    (menu_stack_item[2] as unknown as MenuStatementData).item_list.push({
      text: ctx.slicedArgs[1].str,
      position: [ctx.currentPath, ctx.currentLineIndex],
      enable_js_expression,
    });
  });

  // analyse menu end statement
  core.script.useAnalyseStatement((ctx, next) => {
    if (ctx.rawtext.trim().split(/ +/).join(" ") !== "end menu") return next();
    // console.log("[HZEngine] Menu analyse end");

    let data: MenuStatementData = ctx.endStatement(
      "menu"
    ) as unknown as MenuStatementData;
    // the data here is {} at first, so dont read its properties, because the properties are not defined yet
    // we will define the properties here
    data.end_position = [ctx.currentPath, ctx.currentLineIndex];
    // set and save the menu data
    ctx.setStatementData(data, [
      data.start_position[0],
      data.start_position[1],
    ]);
  });

  function buildMenuViewProp(menu_data: MenuStatementData): UI.MenuViewProp {
    // parse menu item string
    let parsed_item_list: UI.MenuItemData[] = [];
    for (let item of menu_data.item_list) {
      parsed_item_list.push({ ...item, text: core.script.parseString(item.text) });
    }
    return { itemList: parsed_item_list };
  }
}

interface MenuStatementData {
  start_position: [path: string, index: number];
  end_position: [path: string, index: number];
  item_list: UI.MenuItemData[];
}
