/**
 * @fileoverview Definição e registro dos blocos Blockly para o jogo Ordenação.
 *
 * Adota operações de lista zero-based alinhadas ao estudo:
 *   - lista_tamanho
 *   - lista_ler(indice)
 *   - lista_escrever(indice, valor)
 *   - lista_trocar(i, j)
 *   - lista_comparar(i, j, operador)
 *
 * @module games.ordenacao.blocks.blocks
 */

"use strict";

import * as Blockly from "blockly/core";
import "blockly/blocks";
import { javascriptGenerator } from "blockly/javascript";
import { CORES_BLOCKLY, CORES_CUSTOMIZADAS } from "@/blockly/blocklyColors";

const COR_LISTA = CORES_BLOCKLY.LISTAS;

export const registerBlocks = () => {
  _defineBlocks();
  _defineGenerators();
};

/**
 * Gera o objeto toolbox com blocos nativos + lista customizada.
 */
export const generateDynamicToolbox = (allowedBlocks = []) => {
  const contents = [];

  const listBlocks = [];

  if (allowedBlocks.includes("ord_lista")) {
    listBlocks.push({ kind: "block", type: "ord_lista" });
  }

  if (allowedBlocks.includes("ord_lista_tamanho")) {
    listBlocks.push({ kind: "block", type: "ord_lista_tamanho" });
  }

  if (allowedBlocks.includes("ord_lista_ler")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_ler",
      inputs: {
        INDICE: { shadow: { type: "math_number", fields: { NUM: 0 } } },
      },
    });
  }

  if (allowedBlocks.includes("ord_lista_escrever")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_escrever",
      inputs: {
        INDICE: { shadow: { type: "math_number", fields: { NUM: 0 } } },
        VALOR: { shadow: { type: "math_number", fields: { NUM: 0 } } },
      },
    });
  }

  if (allowedBlocks.includes("ord_lista_trocar")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_trocar",
      inputs: {
        I: { shadow: { type: "math_number", fields: { NUM: 0 } } },
        J: { shadow: { type: "math_number", fields: { NUM: 1 } } },
      },
    });
  }

  if (allowedBlocks.includes("ord_lista_comparar")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_comparar",
      inputs: {
        I: { shadow: { type: "math_number", fields: { NUM: 0 } } },
        J: { shadow: { type: "math_number", fields: { NUM: 1 } } },
      },
    });
  }

  if (allowedBlocks.includes("ord_lista_contagem")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_contagem",
    });
  }

  if (allowedBlocks.includes("ord_lista_contagem_ler")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_contagem_ler",
      inputs: {
        INDICE: { shadow: { type: "math_number", fields: { NUM: 0 } } },
      },
    });
  }

  if (allowedBlocks.includes("ord_lista_contagem_escrever")) {
    listBlocks.push({
      kind: "block",
      type: "ord_lista_contagem_escrever",
      inputs: {
        INDICE: { shadow: { type: "math_number", fields: { NUM: 0 } } },
        VALOR: { shadow: { type: "math_number", fields: { NUM: 0 } } },
      },
    });
  }

  if (listBlocks.length > 0) {
    contents.push({
      kind: "category",
      name: "Listas",
      colour: COR_LISTA,
      contents: listBlocks,
    });
  }

  if (allowedBlocks.some(b => ["logic_compare", "logic_boolean", "logic_negate", "logic_operation", "controls_if"].includes(b))) {
    const logica = [];
    if (allowedBlocks.includes("logic_compare")) {
      logica.push({ kind: "block", type: "logic_compare" });
    }
    if (allowedBlocks.includes("logic_boolean")) {
      logica.push({ kind: "block", type: "logic_boolean" });
    }
    if (allowedBlocks.includes("logic_negate")) {
      logica.push({ kind: "block", type: "logic_negate" });
    }
    if (allowedBlocks.includes("controls_if")) {
      logica.push({ kind: "block", type: "controls_if" });
    }
    if (allowedBlocks.includes("logic_operation")) {
      logica.push({ kind: "block", type: "logic_operation" });
    }
    if (logica.length > 0) {
      contents.push({
        kind: "category",
        name: "Lógica",
        colour: CORES_BLOCKLY.LOGICA,
        contents: logica,
      });
    }
  }

  if (allowedBlocks.some((b) => ["controls_for", "controls_while", "ord_for_custom"].includes(b))) {
    const repeticao = [];

    if (allowedBlocks.includes("ord_for_custom")) {
      repeticao.push({
        kind: "block",
        type: "ord_for_custom",
        inputs: {
          FROM: { shadow: { type: "math_number", fields: { NUM: 0 } } },
        },
      });
    }

    if (allowedBlocks.includes("controls_for")) {
      repeticao.push({
        kind: "block",
        type: "controls_for",
      });
    }

    if (allowedBlocks.includes("controls_while")) {
      repeticao.push({ kind: "block", type: "controls_whileUntil" });
    }

    if (allowedBlocks.includes("controls_flow_statements")) {
      repeticao.push({ kind: "block", type: "ord_break" });
    }

    contents.push({
      kind: "category",
      name: "Repetição",
      colour: CORES_BLOCKLY.LOOPS,
      contents: repeticao,
    });
  }

  if (allowedBlocks.includes("variables")) {
    contents.push({
      kind: "category",
      name: "Variáveis",
      colour: CORES_BLOCKLY.VARIAVEIS,
      custom: "VARIABLE",
    });
  }

  if (allowedBlocks.some(b => ["math_number", "math_arithmetic", "math_round"].includes(b))) {
    const math = [];
    if (allowedBlocks.includes("math_number")) {
      math.push({ kind: "block", type: "math_number" });
    }
    if (allowedBlocks.includes("math_arithmetic")) {
      math.push({ kind: "block", type: "math_arithmetic" });
    }
    if (allowedBlocks.includes("math_round")) {
      math.push({ kind: "block", type: "math_round" });
    }
    if (math.length > 0) {
      contents.push({
        kind: "category",
        name: "Matemática",
        colour: CORES_BLOCKLY.MATEMATICA,
        contents: math,
      });
    }
  }

  return { kind: "categoryToolbox", contents };
};

const _defineBlocks = () => {
  Blockly.Blocks["ord_lista"] = {
    init() {
      this.appendDummyInput().appendField("LISTA DE NÚMEROS");
      this.setOutput(true, "Array");
      this.setColour(COR_LISTA);
      this.setTooltip("A lista de números para ordenar.");
    },
  };

  Blockly.Blocks["ord_lista_contagem"] = {
    init() {
      this.appendDummyInput().appendField("LISTA CONTAGEM");
      this.setOutput(true, "Array");
      this.setColour("#6d4c41");
      this.setTooltip("Array auxiliar 'contagem' para Counting Sort.");
    },
  };

    Blockly.Blocks["ord_lista_contagem_ler"] = {
    init() {
      this.appendValueInput("INDICE")
        .setCheck("Number")
        .appendField("lista contagem ler POSICAO");
      this.setOutput(true, null);
      this.setColour(COR_LISTA);
      this.setTooltip("Lê lista[indice] com índice zero-based.");
    },
  };

    Blockly.Blocks["ord_lista_contagem_escrever"] = {
    init() {
      this.appendValueInput("INDICE")
        .setCheck("Number")
        .appendField("lista contagem escrever POSICAO");
      this.appendValueInput("VALOR")
        .appendField("valor");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(COR_LISTA);
      this.setTooltip("Escreve em lista[indice] com índice zero-based.");
    },
  };

  Blockly.Blocks["ord_lista_tamanho"] = {
    init() {
      this.appendDummyInput().appendField("lista tamanho");
      this.setOutput(true, "Number");
      this.setColour(COR_LISTA);
      this.setTooltip("Retorna o tamanho da lista (índice inicia em 0).");
    },
  };

  Blockly.Blocks["ord_lista_ler"] = {
    init() {
      this.appendValueInput("INDICE")
        .setCheck("Number")
        .appendField("lista ler POSICAO");
      this.setOutput(true, null);
      this.setColour(COR_LISTA);
      this.setTooltip("Lê lista[indice] com índice zero-based.");
    },
  };

  Blockly.Blocks["ord_lista_escrever"] = {
    init() {
      this.appendValueInput("INDICE")
        .setCheck("Number")
        .appendField("lista escrever POSICAO");
      this.appendValueInput("VALOR")
        .appendField("valor");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(COR_LISTA);
      this.setTooltip("Escreve em lista[indice] com índice zero-based.");
    },
  };

  Blockly.Blocks["ord_lista_trocar"] = {
    init() {
      this.appendValueInput("I")
        .setCheck("Number")
        .appendField("lista trocar POSICAO");
      this.appendValueInput("J")
        .setCheck("Number")
        .appendField("com POSICAO");
      this.setInputsInline(true);
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(COR_LISTA);
      this.setTooltip("Troca os valores de lista[i] e lista[j].");
    },
  };

  Blockly.Blocks["ord_for_custom"] = {
    init() {
      this.appendValueInput("FROM")
        .setCheck("Number")
        .appendField("PARA")
        .appendField(new Blockly.FieldVariable("i"), "VAR")
        .appendField("=");
      this.appendValueInput("COND")
        .setCheck("Boolean")
        .appendField("enquanto");
      this.appendValueInput("UPDATE")
        .appendField("incremento");
      this.appendStatementInput("DO")
        .appendField("fazer");
      this.setInputsInline(true);
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(CORES_BLOCKLY.LOOPS);
      this.setTooltip("Loop PARA estilo JavaScript: variável = início; enquanto condição; incremento.");
    },
  };

  Blockly.Blocks["ord_break"] = {
    init() {
      this.appendDummyInput().appendField("parar loop");
      this.setPreviousStatement(true, null);
      this.setNextStatement(true, null);
      this.setColour(CORES_BLOCKLY.LOOPS);
      this.setTooltip("Interrompe o loop atual (break).");
    },
  };

  Blockly.Blocks["ord_lista_comparar"] = {
    init() {
      this.appendValueInput("I")
        .setCheck("Number")
        .appendField("lista POSICAO");
      this.appendDummyInput()
        .appendField(new Blockly.FieldDropdown([
          [">", ">"],
          ["<", "<"],
          [">=", ">="],
          ["<=", "<="],
          ["==", "=="],
          ["!=", "!="],
        ]), "OP");
      this.appendValueInput("J")
        .setCheck("Number")
        .appendField("lista POSICAO");
      this.setOutput(true, "Boolean");
      this.setColour(COR_LISTA);
      this.setTooltip("Compara lista[i] OP lista[j].");
    },
  };

};

const _defineGenerators = () => {
  javascriptGenerator.STATEMENT_PREFIX = "highlightBlock(%1);\n";
  javascriptGenerator.addReservedWords("highlightBlock,lista,contagem,lista_tamanho,lista_ler,lista_escrever,lista_trocar,lista_comparar");

  javascriptGenerator.forBlock["ord_lista"] = function () {
    return ["lista", javascriptGenerator.ORDER_MEMBER];
  };

  javascriptGenerator.forBlock["ord_lista_contagem"] = function () {
    return ["contagem", javascriptGenerator.ORDER_ATOMIC];
  };

  javascriptGenerator.forBlock["ord_lista_tamanho"] = function () {
    return ["lista_tamanho()", javascriptGenerator.ORDER_FUNCTION_CALL];
  };

  javascriptGenerator.forBlock["ord_lista_contagem_ler"] = function (block, generator) {
    const index = generator.valueToCode(block, "INDICE", generator.ORDER_NONE) ?? "0";
    return [`lista_contagem_ler(${index})`, generator.ORDER_FUNCTION_CALL];
  };

  javascriptGenerator.forBlock["ord_lista_contagem_escrever"] = function (block, generator) {
    const index = generator.valueToCode(block, "INDICE", generator.ORDER_NONE) ?? "0";
    const value = generator.valueToCode(block, "VALOR", generator.ORDER_NONE) ?? "0";
    return `lista_contagem_escrever(${index}, ${value});\n`;
  };

  javascriptGenerator.forBlock["ord_lista_ler"] = function (block, generator) {
    const index = generator.valueToCode(block, "INDICE", generator.ORDER_NONE) ?? "0";
    return [`lista_ler(${index})`, generator.ORDER_FUNCTION_CALL];
  };

  javascriptGenerator.forBlock["ord_lista_escrever"] = function (block, generator) {
    const index = generator.valueToCode(block, "INDICE", generator.ORDER_NONE) ?? "0";
    const value = generator.valueToCode(block, "VALOR", generator.ORDER_NONE) ?? "0";
    return `lista_escrever(${index}, ${value});\n`;
  };

  javascriptGenerator.forBlock["ord_lista_trocar"] = function (block, generator) {
    const i = generator.valueToCode(block, "I", generator.ORDER_NONE) ?? "0";
    const j = generator.valueToCode(block, "J", generator.ORDER_NONE) ?? "0";
    return `lista_trocar(${i}, ${j});\n`;
  };

  javascriptGenerator.forBlock["ord_break"] = function () {
    return "break;\n";
  };

  javascriptGenerator.forBlock["ord_lista_comparar"] = function (block, generator) {
    const i = generator.valueToCode(block, "I", generator.ORDER_NONE) ?? "0";
    const j = generator.valueToCode(block, "J", generator.ORDER_NONE) ?? "0";
    const op = block.getFieldValue("OP") || ">";
    return [`lista_comparar(${i}, ${j}, "${op}")`, generator.ORDER_FUNCTION_CALL];
  };

  javascriptGenerator.forBlock["ord_for_custom"] = function (block, generator) {
    const varName = generator.nameDB_.getName(
      block.getFieldValue("VAR"),
      Blockly.Names.NameType.VARIABLE
    );
    const from = generator.valueToCode(block, "FROM", generator.ORDER_ASSIGNMENT) || "0";
    const cond = generator.valueToCode(block, "COND", generator.ORDER_NONE) || "false";
    const update = generator.valueToCode(block, "UPDATE", generator.ORDER_ASSIGNMENT) || varName;
    const body = generator.statementToCode(block, "DO");
    return `for (var ${varName} = ${from}; ${cond}; ${varName} = ${update}) {\n${body}}\n`;
  };

};
