import * as Blockly from 'blockly';
import { arduinoGenerator, Order } from './arduino.js';

// ═══════════════════════════════════════════════════════════
//  Logic
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['controls_if'] = function (block) {
  let n = 0;
  let code = '';
  if (arduinoGenerator.STATEMENT_PREFIX) {
    code += arduinoGenerator.injectId(arduinoGenerator.STATEMENT_PREFIX, block);
  }
  do {
    const conditionCode = arduinoGenerator.valueToCode(block, 'IF' + n, Order.NONE) || 'false';
    let branchCode = arduinoGenerator.statementToCode(block, 'DO' + n);
    if (arduinoGenerator.STATEMENT_SUFFIX) {
      branchCode = arduinoGenerator.prefixLines(
        arduinoGenerator.injectId(arduinoGenerator.STATEMENT_SUFFIX, block), arduinoGenerator.INDENT
      ) + branchCode;
    }
    code += (n > 0 ? ' else ' : '') + 'if (' + conditionCode + ') {\n' + branchCode + '}';
    n++;
  } while (block.getInput('IF' + n));
  if (block.getInput('ELSE') || block.getInput('ELSE')) {
    let branchCode = arduinoGenerator.statementToCode(block, 'ELSE');
    if (arduinoGenerator.STATEMENT_SUFFIX) {
      branchCode = arduinoGenerator.prefixLines(
        arduinoGenerator.injectId(arduinoGenerator.STATEMENT_SUFFIX, block), arduinoGenerator.INDENT
      ) + branchCode;
    }
    code += ' else {\n' + branchCode + '}';
  }
  return code + '\n';
};

arduinoGenerator.forBlock['logic_compare'] = function (block) {
  const OPERATORS = {
    EQ: '==', NEQ: '!=', LT: '<', LTE: '<=', GT: '>', GTE: '>=',
  };
  const op = OPERATORS[block.getFieldValue('OP')];
  const order = (op === '==' || op === '!=') ? Order.EQUALITY : Order.RELATIONAL;
  const a = arduinoGenerator.valueToCode(block, 'A', order) || '0';
  const b = arduinoGenerator.valueToCode(block, 'B', order) || '0';
  return [a + ' ' + op + ' ' + b, order];
};

arduinoGenerator.forBlock['logic_operation'] = function (block) {
  const op = block.getFieldValue('OP') === 'AND' ? '&&' : '||';
  const order = op === '&&' ? Order.LOGICAL_AND : Order.LOGICAL_OR;
  let a = arduinoGenerator.valueToCode(block, 'A', order);
  let b = arduinoGenerator.valueToCode(block, 'B', order);
  if (!a && !b) {
    a = 'false';
    b = 'false';
  } else {
    if (!a) a = 'true';
    if (!b) b = 'true';
  }
  return [a + ' ' + op + ' ' + b, order];
};

arduinoGenerator.forBlock['logic_negate'] = function (block) {
  const order = Order.UNARY_PREFIX;
  const arg = arduinoGenerator.valueToCode(block, 'BOOL', order) || 'true';
  return ['!' + arg, order];
};

arduinoGenerator.forBlock['logic_boolean'] = function (block) {
  return [block.getFieldValue('BOOL') === 'TRUE' ? 'true' : 'false', Order.ATOMIC];
};

arduinoGenerator.forBlock['logic_null'] = function () {
  return ['NULL', Order.ATOMIC];
};

arduinoGenerator.forBlock['logic_ternary'] = function (block) {
  const cond = arduinoGenerator.valueToCode(block, 'IF', Order.CONDITIONAL) || 'false';
  const thenVal = arduinoGenerator.valueToCode(block, 'THEN', Order.CONDITIONAL) || 'NULL';
  const elseVal = arduinoGenerator.valueToCode(block, 'ELSE', Order.CONDITIONAL) || 'NULL';
  return ['(' + cond + ' ? ' + thenVal + ' : ' + elseVal + ')', Order.CONDITIONAL];
};

// ═══════════════════════════════════════════════════════════
//  Loops
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['controls_repeat_ext'] = function (block) {
  let repeats = arduinoGenerator.valueToCode(block, 'TIMES', Order.ASSIGNMENT) || '0';
  let branch = arduinoGenerator.statementToCode(block, 'DO');
  branch = arduinoGenerator.addLoopTrap(branch, block);
  let loopVar = arduinoGenerator.nameDB_.getDistinctName('count', Blockly.Names.NameType.VARIABLE);
  return `for (int ${loopVar} = 0; ${loopVar} < ${repeats}; ${loopVar}++) {\n${branch}}\n`;
};

arduinoGenerator.forBlock['controls_whileUntil'] = function (block) {
  const until = block.getFieldValue('MODE') === 'UNTIL';
  let cond = arduinoGenerator.valueToCode(block, 'BOOL', until ? Order.UNARY_PREFIX : Order.NONE) || 'false';
  let branch = arduinoGenerator.statementToCode(block, 'DO');
  branch = arduinoGenerator.addLoopTrap(branch, block);
  if (until) {
    cond = '!' + cond;
  }
  return `while (${cond}) {\n${branch}}\n`;
};

arduinoGenerator.forBlock['controls_for'] = function (block) {
  const variable0 = arduinoGenerator.getVariableName(block.getFieldValue('VAR'));
  const from_ = arduinoGenerator.valueToCode(block, 'FROM', Order.ASSIGNMENT) || '0';
  const to = arduinoGenerator.valueToCode(block, 'TO', Order.ASSIGNMENT) || '0';
  const by = arduinoGenerator.valueToCode(block, 'BY', Order.ASSIGNMENT) || '1';
  return `for (int ${variable0} = ${from_}; ${variable0} <= ${to}; ${variable0} += ${by}) {\n` +
    arduinoGenerator.statementToCode(block, 'DO') + '}\n';
};

arduinoGenerator.forBlock['controls_forEach'] = function (block) {
  // C++ for-each is limited; generate index-based loop comment
  const variable0 = arduinoGenerator.getVariableName(block.getFieldValue('VAR'));
  const list = arduinoGenerator.valueToCode(block, 'LIST', Order.ASSIGNMENT) || '""';
  let branch = arduinoGenerator.statementToCode(block, 'DO');
  return `// for-each not directly supported; use indexed loop\n`;
};

arduinoGenerator.forBlock['controls_flow_statements'] = function (block) {
  switch (block.getFieldValue('FLOW')) {
    case 'BREAK': return 'break;\n';
    case 'CONTINUE': return 'continue;\n';
  }
  return '';
};

// ═══════════════════════════════════════════════════════════
//  Math
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['math_number'] = function (block) {
  const num = Number(block.getFieldValue('NUM'));
  const order = num >= 0 ? Order.ATOMIC : Order.UNARY_PREFIX;
  return [String(num), order];
};

arduinoGenerator.forBlock['math_arithmetic'] = function (block) {
  const OPERATORS = {
    ADD: [' + ', Order.ADDITIVE],
    MINUS: [' - ', Order.ADDITIVE],
    MULTIPLY: [' * ', Order.MULTIPLICATIVE],
    DIVIDE: [' / ', Order.MULTIPLICATIVE],
    POWER: [null, Order.FUNCTION_CALL],
  };
  const tuple = OPERATORS[block.getFieldValue('OP')];
  const op = tuple[0];
  const order = tuple[1];
  const a = arduinoGenerator.valueToCode(block, 'A', order) || '0';
  const b = arduinoGenerator.valueToCode(block, 'B', order) || '0';
  if (!op) {
    return [`pow(${a}, ${b})`, Order.FUNCTION_CALL];
  }
  return [a + op + b, order];
};

arduinoGenerator.forBlock['math_single'] = function (block) {
  const op = block.getFieldValue('OP');
  let arg = arduinoGenerator.valueToCode(block, 'NUM', Order.NONE) || '0';
  arduinoGenerator.addInclude('include_math', '#include <math.h>');
  switch (op) {
    case 'ROOT': return [`sqrt(${arg})`, Order.FUNCTION_CALL];
    case 'ABS': return [`abs(${arg})`, Order.FUNCTION_CALL];
    case 'NEG': return [`-${arg}`, Order.UNARY_PREFIX];
    case 'LN': return [`log(${arg})`, Order.FUNCTION_CALL];
    case 'LOG10': return [`log10(${arg})`, Order.FUNCTION_CALL];
    case 'EXP': return [`exp(${arg})`, Order.FUNCTION_CALL];
    case 'POW10': return [`pow(10, ${arg})`, Order.FUNCTION_CALL];
  }
  return [arg, Order.NONE];
};

arduinoGenerator.forBlock['math_trig'] = function (block) {
  const op = block.getFieldValue('OP');
  const arg = arduinoGenerator.valueToCode(block, 'NUM', Order.NONE) || '0';
  arduinoGenerator.addInclude('include_math', '#include <math.h>');
  const RAD = `(${arg} * M_PI / 180.0)`;
  switch (op) {
    case 'SIN': return [`sin(${RAD})`, Order.FUNCTION_CALL];
    case 'COS': return [`cos(${RAD})`, Order.FUNCTION_CALL];
    case 'TAN': return [`tan(${RAD})`, Order.FUNCTION_CALL];
    case 'ASIN': return [`(asin(${arg}) * 180.0 / M_PI)`, Order.MULTIPLICATIVE];
    case 'ACOS': return [`(acos(${arg}) * 180.0 / M_PI)`, Order.MULTIPLICATIVE];
    case 'ATAN': return [`(atan(${arg}) * 180.0 / M_PI)`, Order.MULTIPLICATIVE];
  }
  return ['0', Order.ATOMIC];
};

arduinoGenerator.forBlock['math_constant'] = function (block) {
  arduinoGenerator.addInclude('include_math', '#include <math.h>');
  const CONSTANTS = {
    PI: ['M_PI', Order.ATOMIC],
    E: ['M_E', Order.ATOMIC],
    GOLDEN_RATIO: ['1.61803398875', Order.ATOMIC],
    SQRT2: ['M_SQRT2', Order.ATOMIC],
    SQRT1_2: ['M_SQRT1_2', Order.ATOMIC],
    INFINITY: ['INFINITY', Order.ATOMIC],
  };
  return CONSTANTS[block.getFieldValue('CONSTANT')] || ['0', Order.ATOMIC];
};

arduinoGenerator.forBlock['math_number_property'] = function (block) {
  const property = block.getFieldValue('PROPERTY');
  const num = arduinoGenerator.valueToCode(block, 'NUMBER_TO_CHECK', Order.MULTIPLICATIVE) || '0';
  switch (property) {
    case 'EVEN': return [`((int)(${num}) % 2 == 0)`, Order.EQUALITY];
    case 'ODD': return [`((int)(${num}) % 2 == 1)`, Order.EQUALITY];
    case 'PRIME':
      arduinoGenerator.addDeclaration('_is_prime',
        'bool _is_prime(int n) {\n' +
        '  if (n < 2) return false;\n' +
        '  for (int i = 2; i * i <= n; i++) { if (n % i == 0) return false; }\n' +
        '  return true;\n' +
        '}'
      );
      return [`_is_prime((int)(${num}))`, Order.FUNCTION_CALL];
    case 'WHOLE': return [`(${num} == (int)(${num}))`, Order.EQUALITY];
    case 'POSITIVE': return [`(${num} > 0)`, Order.RELATIONAL];
    case 'NEGATIVE': return [`(${num} < 0)`, Order.RELATIONAL];
    case 'DIVISIBLE_BY':
      const divisor = arduinoGenerator.valueToCode(block, 'DIVISOR', Order.MULTIPLICATIVE) || '1';
      return [`((int)(${num}) % (int)(${divisor}) == 0)`, Order.EQUALITY];
  }
  return ['false', Order.ATOMIC];
};

arduinoGenerator.forBlock['math_round'] = function (block) {
  const op = block.getFieldValue('OP');
  const arg = arduinoGenerator.valueToCode(block, 'NUM', Order.NONE) || '0';
  arduinoGenerator.addInclude('include_math', '#include <math.h>');
  switch (op) {
    case 'ROUND': return [`round(${arg})`, Order.FUNCTION_CALL];
    case 'ROUNDUP': return [`ceil(${arg})`, Order.FUNCTION_CALL];
    case 'ROUNDDOWN': return [`floor(${arg})`, Order.FUNCTION_CALL];
  }
  return [arg, Order.NONE];
};

arduinoGenerator.forBlock['math_modulo'] = function (block) {
  const a = arduinoGenerator.valueToCode(block, 'DIVIDEND', Order.MULTIPLICATIVE) || '0';
  const b = arduinoGenerator.valueToCode(block, 'DIVISOR', Order.MULTIPLICATIVE) || '0';
  return [`(int)(${a}) % (int)(${b})`, Order.MULTIPLICATIVE];
};

arduinoGenerator.forBlock['math_constrain'] = function (block) {
  const val = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
  const low = arduinoGenerator.valueToCode(block, 'LOW', Order.NONE) || '0';
  const high = arduinoGenerator.valueToCode(block, 'HIGH', Order.NONE) || '100';
  return [`constrain(${val}, ${low}, ${high})`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['math_random_int'] = function (block) {
  const from_ = arduinoGenerator.valueToCode(block, 'FROM', Order.NONE) || '0';
  const to = arduinoGenerator.valueToCode(block, 'TO', Order.ADDITIVE) || '100';
  return [`random(${from_}, ${to} + 1)`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['math_random_float'] = function () {
  return ['(random(10001) / 10000.0)', Order.MULTIPLICATIVE];
};

// ═══════════════════════════════════════════════════════════
//  Text
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['text'] = function (block) {
  return [arduinoGenerator.quote_(block.getFieldValue('TEXT')), Order.ATOMIC];
};

arduinoGenerator.forBlock['text_join'] = function (block) {
  const count = block.itemCount_;
  if (count === 0) {
    return ['String("")', Order.FUNCTION_CALL];
  }
  if (count === 1) {
    const element = arduinoGenerator.valueToCode(block, 'ADD0', Order.NONE) || '""';
    return [`String(${element})`, Order.FUNCTION_CALL];
  }
  const elements = [];
  for (let i = 0; i < count; i++) {
    elements.push(arduinoGenerator.valueToCode(block, 'ADD' + i, Order.NONE) || '""');
  }
  // Build concatenation using String()
  let code = `String(${elements[0]})`;
  for (let i = 1; i < elements.length; i++) {
    code += ` + String(${elements[i]})`;
  }
  return [code, Order.ADDITIVE];
};

arduinoGenerator.forBlock['text_append'] = function (block) {
  const varName = arduinoGenerator.getVariableName(block.getFieldValue('VAR'));
  const value = arduinoGenerator.valueToCode(block, 'TEXT', Order.NONE) || '""';
  return `${varName} += String(${value});\n`;
};

arduinoGenerator.forBlock['text_length'] = function (block) {
  const text = arduinoGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '""';
  return [`String(${text}).length()`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['text_isEmpty'] = function (block) {
  const text = arduinoGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '""';
  return [`(String(${text}).length() == 0)`, Order.EQUALITY];
};

arduinoGenerator.forBlock['text_indexOf'] = function (block) {
  const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf';
  const substr = arduinoGenerator.valueToCode(block, 'FIND', Order.NONE) || '""';
  const text = arduinoGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '""';
  return [`String(${text}).${operator}(String(${substr}))`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['text_charAt'] = function (block) {
  const where = block.getFieldValue('WHERE') || 'FROM_START';
  const text = arduinoGenerator.valueToCode(block, 'VALUE', Order.MEMBER) || '""';
  let at;
  switch (where) {
    case 'FROM_START':
      at = arduinoGenerator.valueToCode(block, 'AT', Order.NONE) || '1';
      return [`String(String(${text}).charAt(${at} - 1))`, Order.FUNCTION_CALL];
    case 'FROM_END':
      at = arduinoGenerator.valueToCode(block, 'AT', Order.NONE) || '1';
      return [`String(String(${text}).charAt(String(${text}).length() - ${at}))`, Order.FUNCTION_CALL];
    case 'FIRST':
      return [`String(String(${text}).charAt(0))`, Order.FUNCTION_CALL];
    case 'LAST':
      return [`String(String(${text}).charAt(String(${text}).length() - 1))`, Order.FUNCTION_CALL];
    case 'RANDOM':
      return [`String(String(${text}).charAt(random(String(${text}).length())))`, Order.FUNCTION_CALL];
  }
  return ['""', Order.ATOMIC];
};

// ═══════════════════════════════════════════════════════════
//  Variables
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['variables_get'] = function (block) {
  const varName = arduinoGenerator.getVariableName(block.getFieldValue('VAR'));
  return [varName, Order.ATOMIC];
};

arduinoGenerator.forBlock['variables_set'] = function (block) {
  const varName = arduinoGenerator.getVariableName(block.getFieldValue('VAR'));
  const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0';
  return `${varName} = ${value};\n`;
};

// ═══════════════════════════════════════════════════════════
//  Functions / Procedures
// ═══════════════════════════════════════════════════════════

arduinoGenerator.forBlock['procedures_defreturn'] = function (block) {
  const funcName = arduinoGenerator.getProcedureName(block.getFieldValue('NAME'));
  let xfix1 = '';
  if (arduinoGenerator.STATEMENT_PREFIX) {
    xfix1 += arduinoGenerator.injectId(arduinoGenerator.STATEMENT_PREFIX, block);
  }
  if (arduinoGenerator.STATEMENT_SUFFIX) {
    xfix1 += arduinoGenerator.injectId(arduinoGenerator.STATEMENT_SUFFIX, block);
  }
  if (xfix1) xfix1 = arduinoGenerator.prefixLines(xfix1, arduinoGenerator.INDENT);

  let branch = arduinoGenerator.statementToCode(block, 'STACK');
  let returnValue = arduinoGenerator.valueToCode(block, 'RETURN', Order.NONE) || '';
  let xfix2 = '';
  if (branch && returnValue) {
    xfix2 = xfix1;
  }
  if (returnValue) {
    returnValue = arduinoGenerator.INDENT + 'return ' + returnValue + ';\n';
  }

  const args = [];
  const variables = block.getVars();
  for (let i = 0; i < variables.length; i++) {
    args.push('float ' + arduinoGenerator.getVariableName(variables[i]));
  }

  const returnType = returnValue ? 'float' : 'void';
  let code = `${returnType} ${funcName}(${args.join(', ')}) {\n${xfix1}${branch}${xfix2}${returnValue}}`;
  arduinoGenerator.functionDefinitions_[funcName] = code;
  return null;
};

arduinoGenerator.forBlock['procedures_defnoreturn'] =
  arduinoGenerator.forBlock['procedures_defreturn'];

arduinoGenerator.forBlock['procedures_callreturn'] = function (block) {
  const funcName = arduinoGenerator.getProcedureName(block.getFieldValue('NAME'));
  const args = [];
  const variables = block.getVars();
  for (let i = 0; i < variables.length; i++) {
    args.push(arduinoGenerator.valueToCode(block, 'ARG' + i, Order.NONE) || '0');
  }
  return [`${funcName}(${args.join(', ')})`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['procedures_callnoreturn'] = function (block) {
  const funcName = arduinoGenerator.getProcedureName(block.getFieldValue('NAME'));
  const args = [];
  const variables = block.getVars();
  for (let i = 0; i < variables.length; i++) {
    args.push(arduinoGenerator.valueToCode(block, 'ARG' + i, Order.NONE) || '0');
  }
  return `${funcName}(${args.join(', ')});\n`;
};

arduinoGenerator.forBlock['procedures_ifreturn'] = function (block) {
  const cond = arduinoGenerator.valueToCode(block, 'CONDITION', Order.NONE) || 'false';
  let code = 'if (' + cond + ') {\n';
  if (arduinoGenerator.STATEMENT_SUFFIX) {
    code += arduinoGenerator.prefixLines(
      arduinoGenerator.injectId(arduinoGenerator.STATEMENT_SUFFIX, block), arduinoGenerator.INDENT
    );
  }
  if (block.hasReturnValue_) {
    const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
    code += arduinoGenerator.INDENT + 'return ' + value + ';\n';
  } else {
    code += arduinoGenerator.INDENT + 'return;\n';
  }
  code += '}\n';
  return code;
};
