import { arduinoGenerator, Order } from '../generators/arduino.js';

// ─── Pin I/O ──────────────────────────────────────────────

arduinoGenerator.forBlock['pin_set_mode'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const mode = block.getFieldValue('MODE');
  const modeMap = { OUT: 'OUTPUT', IN: 'INPUT', IN_PULLUP: 'INPUT_PULLUP' };
  arduinoGenerator.addSetupCode(`pinMode(${pin}, ${modeMap[mode] || 'OUTPUT'});`);
  return '';
};

arduinoGenerator.forBlock['pin_digital_write'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const value = block.getFieldValue('VALUE');
  return `digitalWrite(${pin}, ${value === '1' ? 'HIGH' : 'LOW'});\n`;
};

arduinoGenerator.forBlock['pin_digital_read'] = function (block) {
  const pin = block.getFieldValue('PIN');
  return [`digitalRead(${pin})`, Order.FUNCTION_CALL];
};

// ─── PWM ──────────────────────────────────────────────────

arduinoGenerator.forBlock['pwm_init'] = function () {
  return '';
};

arduinoGenerator.forBlock['pwm_set_duty'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const duty = arduinoGenerator.valueToCode(block, 'DUTY', Order.NONE) || '0';
  return `analogWrite(${pin}, ${duty});\n`;
};

arduinoGenerator.forBlock['pwm_set_freq'] = function () {
  return '// PWM frequency change not supported on AVR\n';
};

// ─── ADC ──────────────────────────────────────────────────

arduinoGenerator.forBlock['adc_read'] = function (block) {
  const pin = block.getFieldValue('PIN');
  return [`analogRead(${pin})`, Order.FUNCTION_CALL];
};

// ─── Sensors (sonar / HC-SR04) ────────────────────────────

arduinoGenerator.forBlock['sonar_distance'] = function (block) {
  const trig = arduinoGenerator.valueToCode(block, 'TRIG', Order.NONE) || '2';
  const echo = arduinoGenerator.valueToCode(block, 'ECHO', Order.NONE) || '3';

  arduinoGenerator.addDeclaration('sonar_helper',
    'float _sonar_cm(int trigPin, int echoPin) {\n' +
    '  pinMode(trigPin, OUTPUT);\n' +
    '  pinMode(echoPin, INPUT);\n' +
    '  digitalWrite(trigPin, LOW);\n' +
    '  delayMicroseconds(2);\n' +
    '  digitalWrite(trigPin, HIGH);\n' +
    '  delayMicroseconds(10);\n' +
    '  digitalWrite(trigPin, LOW);\n' +
    '  long d = pulseIn(echoPin, HIGH, 30000);\n' +
    '  return d > 0 ? d / 58.0 : -1;\n' +
    '}'
  );

  return [`_sonar_cm(${trig}, ${echo})`, Order.FUNCTION_CALL];
};

// ─── Time ─────────────────────────────────────────────────

arduinoGenerator.forBlock['sleep_seconds'] = function (block) {
  const seconds = arduinoGenerator.valueToCode(block, 'SECONDS', Order.MULTIPLICATIVE) || '1';
  return `delay(${seconds} * 1000);\n`;
};

arduinoGenerator.forBlock['sleep_ms'] = function (block) {
  const ms = arduinoGenerator.valueToCode(block, 'MS', Order.NONE) || '100';
  return `delay(${ms});\n`;
};

arduinoGenerator.forBlock['ticks_ms'] = function () {
  return ['millis()', Order.FUNCTION_CALL];
};

// ─── Random ───────────────────────────────────────────────

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

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

arduinoGenerator.forBlock['random_uniform'] = function (block) {
  const low = arduinoGenerator.valueToCode(block, 'LOW', Order.NONE) || '0';
  const high = arduinoGenerator.valueToCode(block, 'HIGH', Order.NONE) || '1';
  return [`(${low} + (random(10001) / 10000.0) * (${high} - ${low}))`, Order.ADDITIVE];
};

arduinoGenerator.forBlock['random_seed'] = function (block) {
  const seed = arduinoGenerator.valueToCode(block, 'SEED', Order.NONE) || '0';
  return `randomSeed(${seed});\n`;
};

// ─── Print ────────────────────────────────────────────────

arduinoGenerator.forBlock['print_text'] = function (block) {
  arduinoGenerator.addSetupCode('Serial.begin(9600);');
  const text = arduinoGenerator.valueToCode(block, 'TEXT', Order.NONE) || '""';
  return `Serial.println(${text});\n`;
};

// ─── H-Bridge Motor ─────────────────────────────────────

arduinoGenerator.definitions_['hb_set_motor_fn'] = [
  'void _hb_set_motor(int p1, int p2, int speed) {',
  '  speed = constrain(speed, -255, 255);',
  '  if (speed > 0) { analogWrite(p1, abs(speed)); analogWrite(p2, 0); }',
  '  else if (speed < 0) { analogWrite(p1, 0); analogWrite(p2, abs(speed)); }',
  '  else { analogWrite(p1, 0); analogWrite(p2, 0); }',
  '}',
].join('\n');

arduinoGenerator.forBlock['hbridge_motor_init'] = function (block) {
  var m = block.getFieldValue('MOTOR').toLowerCase();
  var in1 = block.getFieldValue('IN1');
  var in2 = block.getFieldValue('IN2');
  arduinoGenerator.definitions_['hb_motor_' + m + '_in1'] = 'const int motor_' + m + '_in1 = ' + in1 + ';';
  arduinoGenerator.definitions_['hb_motor_' + m + '_in2'] = 'const int motor_' + m + '_in2 = ' + in2 + ';';
  arduinoGenerator.addSetupCode('pinMode(' + in1 + ', OUTPUT);');
  arduinoGenerator.addSetupCode('pinMode(' + in2 + ', OUTPUT);');
  return '';
};

arduinoGenerator.forBlock['hbridge_motor_speed'] = function (block) {
  var m = block.getFieldValue('MOTOR').toLowerCase();
  var speed = arduinoGenerator.valueToCode(block, 'SPEED', Order.NONE) || '0';
  return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, ' + speed + ');\n';
};

arduinoGenerator.forBlock['hbridge_motor_stop'] = function (block) {
  var m = block.getFieldValue('MOTOR').toLowerCase();
  return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, 0);\n';
};

arduinoGenerator.forBlock['hbridge_dual_init'] = function (block) {
  var pins = [
    ['motor_l_in1', 'L_IN1'], ['motor_l_in2', 'L_IN2'],
    ['motor_r_in1', 'R_IN1'], ['motor_r_in2', 'R_IN2'],
  ];
  for (var [name, field] of pins) {
    var pin = block.getFieldValue(field);
    arduinoGenerator.definitions_['hb_' + name] = 'const int ' + name + ' = ' + pin + ';';
    arduinoGenerator.addSetupCode('pinMode(' + pin + ', OUTPUT);');
  }
  return '';
};

arduinoGenerator.forBlock['hbridge_dual_speed'] = function (block) {
  var left = arduinoGenerator.valueToCode(block, 'LEFT', Order.NONE) || '0';
  var right = arduinoGenerator.valueToCode(block, 'RIGHT', Order.NONE) || '0';
  return '_hb_set_motor(motor_l_in1, motor_l_in2, ' + left + ');\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, ' + right + ');\n';
};

arduinoGenerator.forBlock['hbridge_dual_forward'] = function () {
  return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n';
};

arduinoGenerator.forBlock['hbridge_dual_backward'] = function () {
  return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n';
};

arduinoGenerator.forBlock['hbridge_dual_left'] = function () {
  return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n';
};

arduinoGenerator.forBlock['hbridge_dual_right'] = function () {
  return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n';
};

arduinoGenerator.forBlock['hbridge_dual_forward_x_seconds'] = function (block) {
  var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
  return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n' +
         'delay((' + s + ') * 1000);\n' +
         '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};

arduinoGenerator.forBlock['hbridge_dual_backward_x_seconds'] = function (block) {
  var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
  return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n' +
         'delay((' + s + ') * 1000);\n' +
         '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};

arduinoGenerator.forBlock['hbridge_dual_left_x_seconds'] = function (block) {
  var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
  return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n' +
         'delay((' + s + ') * 1000);\n' +
         '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};

arduinoGenerator.forBlock['hbridge_dual_right_x_seconds'] = function (block) {
  var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
  return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n' +
         'delay((' + s + ') * 1000);\n' +
         '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};

arduinoGenerator.forBlock['hbridge_dual_stop'] = function () {
  return '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
         '_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};

// ─── Sound (shared blocks) ────────────────────────────────

arduinoGenerator.forBlock['sound_play_tone'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
  const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  return `tone(${pin}, ${freq}, ${duration});\n`;
};

arduinoGenerator.forBlock['sound_stop'] = function (block) {
  const pin = block.getFieldValue('PIN');
  return `noTone(${pin});\n`;
};

arduinoGenerator.forBlock['sound_play_tone_speaker'] = function () {
  return '// Built-in speaker not available on Arduino\n';
};

arduinoGenerator.forBlock['sound_stop_speaker'] = function () {
  return '// Built-in speaker not available on Arduino\n';
};

arduinoGenerator.forBlock['sound_play_note'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const freq = block.getFieldValue('NOTE');
  const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  return `tone(${pin}, ${freq}, ${duration});\n`;
};

arduinoGenerator.forBlock['sound_play_note_speaker'] = function () {
  return '// Built-in speaker not available on Arduino\n';
};

arduinoGenerator.forBlock['sound_play_staff_note'] =
  arduinoGenerator.forBlock['sound_play_note'];
arduinoGenerator.forBlock['sound_play_staff_note_speaker'] =
  arduinoGenerator.forBlock['sound_play_note_speaker'];

// ─── Sound – buzzer (init + pin-free) ────────────────────

arduinoGenerator.forBlock['sound_buzzer_init'] = function (block) {
  const pin = block.getFieldValue('PIN');
  arduinoGenerator.definitions_['buzzer_pin'] = `const int _buzzerPin = ${pin};`;
  arduinoGenerator.addSetupCode('noTone(_buzzerPin);');
  return '';
};

arduinoGenerator.forBlock['sound_buzzer_tone'] = function (block) {
  const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
  const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  return `tone(_buzzerPin, ${freq}, ${duration});\n`;
};

arduinoGenerator.forBlock['sound_buzzer_note'] = function (block) {
  const freq = block.getFieldValue('NOTE');
  const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  return `tone(_buzzerPin, ${freq}, ${duration});\n`;
};

arduinoGenerator.forBlock['sound_buzzer_staff_note'] =
  arduinoGenerator.forBlock['sound_buzzer_note'];

arduinoGenerator.forBlock['sound_buzzer_stop'] = function () {
  return 'noTone(_buzzerPin);\n';
};

// ─── Sound – melodies ────────────────────────────────────

const MELODIES = {
  mario: [
    [660,100],[660,100],[0,100],[660,100],[0,100],[520,100],[660,100],[0,100],
    [784,100],[0,300],[392,100],[0,300],
    [520,100],[0,200],[392,100],[0,200],[330,100],[0,200],
    [440,100],[0,100],[494,100],[0,100],[466,100],[440,100],[0,100],
    [392,100],[660,100],[784,100],[880,100],[0,100],[698,100],[784,100],
    [0,100],[660,100],[0,100],[520,100],[587,100],[494,100],[0,100],
    [520,100],[0,200],[392,100],[0,200],[330,100],[0,200],
    [440,100],[0,100],[494,100],[0,100],[466,100],[440,100],[0,100],
    [392,100],[660,100],[784,100],[880,100],[0,100],[698,100],[784,100],
    [0,100],[660,100],[0,100],[520,100],[587,100],[494,100],[0,200],
  ],
  happy: [[523,150],[0,30],[659,150],[0,30],[784,200]],
  sad:   [[392,250],[0,30],[330,250],[0,30],[262,300]],
};

function melodyArduino(name, pinExpr) {
  const notes = MELODIES[name];
  const freqArr = `_${name}Freqs`;
  const durArr = `_${name}Durs`;
  const lenVar = `_${name}Len`;
  arduinoGenerator.definitions_[`${name}_freqs`] =
    `const int ${freqArr}[] = {${notes.map(([f]) => f).join(',')}};`;
  arduinoGenerator.definitions_[`${name}_durs`] =
    `const int ${durArr}[] = {${notes.map(([, d]) => d).join(',')}};`;
  arduinoGenerator.definitions_[`${name}_len`] =
    `const int ${lenVar} = ${notes.length};`;
  let code = '';
  code += `for (int _i = 0; _i < ${lenVar}; _i++) {\n`;
  code += `  if (${freqArr}[_i] == 0) {\n`;
  code += `    noTone(${pinExpr});\n`;
  code += `  } else {\n`;
  code += `    tone(${pinExpr}, ${freqArr}[_i]);\n`;
  code += `  }\n`;
  code += `  delay(${durArr}[_i]);\n`;
  code += `}\n`;
  code += `noTone(${pinExpr});\n`;
  return code;
}

function melodyPinArduino(name) {
  return function (block) {
    return melodyArduino(name, String(block.getFieldValue('PIN')));
  };
}

function melodyBuzzerArduino(name) {
  return function () {
    return melodyArduino(name, '_buzzerPin');
  };
}

arduinoGenerator.forBlock['sound_melody_mario'] = melodyPinArduino('mario');
arduinoGenerator.forBlock['sound_melody_mario_buzzer'] = melodyBuzzerArduino('mario');
arduinoGenerator.forBlock['sound_melody_happy'] = melodyPinArduino('happy');
arduinoGenerator.forBlock['sound_melody_happy_buzzer'] = melodyBuzzerArduino('happy');
arduinoGenerator.forBlock['sound_melody_sad'] = melodyPinArduino('sad');
arduinoGenerator.forBlock['sound_melody_sad_buzzer'] = melodyBuzzerArduino('sad');

// ─── Arduino-specific blocks ──────────────────────────────

arduinoGenerator.forBlock['arduino_builtin_led'] = function (block) {
  const state = block.getFieldValue('STATE');
  arduinoGenerator.addSetupCode('pinMode(LED_BUILTIN, OUTPUT);');
  return `digitalWrite(LED_BUILTIN, ${state === '1' ? 'HIGH' : 'LOW'});\n`;
};

arduinoGenerator.forBlock['arduino_analog_write'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
  return `analogWrite(${pin}, ${value});\n`;
};

arduinoGenerator.forBlock['arduino_analog_read'] = function (block) {
  const ch = block.getFieldValue('CHANNEL');
  return [`analogRead(A${ch})`, Order.FUNCTION_CALL];
};

arduinoGenerator.forBlock['arduino_servo_attach'] = function (block) {
  const pin = block.getFieldValue('PIN');
  arduinoGenerator.addInclude('include_servo', '#include <Servo.h>');
  arduinoGenerator.addDeclaration(`servo_decl_${pin}`, `Servo servo_${pin};`);
  arduinoGenerator.addSetupCode(`servo_${pin}.attach(${pin});`);
  return '';
};

arduinoGenerator.forBlock['arduino_servo_write'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const angle = arduinoGenerator.valueToCode(block, 'ANGLE', Order.NONE) || '90';
  arduinoGenerator.addInclude('include_servo', '#include <Servo.h>');
  arduinoGenerator.addDeclaration(`servo_decl_${pin}`, `Servo servo_${pin};`);
  arduinoGenerator.addSetupCode(`servo_${pin}.attach(${pin});`);
  return `servo_${pin}.write(${angle});\n`;
};

arduinoGenerator.forBlock['arduino_tone'] = function (block) {
  const pin = block.getFieldValue('PIN');
  const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
  const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
  return `tone(${pin}, ${freq}, ${duration});\n`;
};

arduinoGenerator.forBlock['arduino_no_tone'] = function (block) {
  const pin = block.getFieldValue('PIN');
  return `noTone(${pin});\n`;
};

arduinoGenerator.forBlock['arduino_map'] = function (block) {
  const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
  const fromLow = arduinoGenerator.valueToCode(block, 'FROM_LOW', Order.NONE) || '0';
  const fromHigh = arduinoGenerator.valueToCode(block, 'FROM_HIGH', Order.NONE) || '1023';
  const toLow = arduinoGenerator.valueToCode(block, 'TO_LOW', Order.NONE) || '0';
  const toHigh = arduinoGenerator.valueToCode(block, 'TO_HIGH', Order.NONE) || '255';
  return [`map(${value}, ${fromLow}, ${fromHigh}, ${toLow}, ${toHigh})`, Order.FUNCTION_CALL];
};

// ─── Blocks not applicable to Arduino ─────────────────────

const unsupportedBlocks = [
  'wifi_connect', 'wifi_get_ip',
  'neopixel_init', 'neopixel_set_color', 'neopixel_show',
  'colour_rgb', 'tuple_create_3', 'tuple_index',
  'i2c_init', 'i2c_scan', 'i2c_writeto', 'i2c_readfrom', 'tcs34725_read_colours',
  'hid_key_press', 'hid_key_down', 'hid_key_up', 'hid_keyboard_type',
  'hid_mouse_move', 'hid_mouse_click', 'hid_mouse_scroll',
  'hid_gamepad_button', 'hid_gamepad_axis',
  'microbit_display_all',
  'superbit_motor_run', 'superbit_motor_stop_all',
];

for (const blockType of unsupportedBlocks) {
  arduinoGenerator.forBlock[blockType] = function () {
    return '// Block not supported on Arduino\n';
  };
}

// ─── Loops ─────────────────────────────────────────────

arduinoGenerator.forBlock['controls_repeat_forever'] = function (block) {
  let branch = arduinoGenerator.statementToCode(block, 'DO');
  return `while (true) {\n${branch}}\n`;
};
