// h-bridge Motor Driver addon
//
// Adds a "h-bridge Motor" toolbox category with:
//   - motor_init: set up a motor with IN1 and IN2 pins
//   - motor_speed: set speed from -255 (full reverse) to +255 (full forward)
//   - motor_stop: stop a motor
//
// Generates device-appropriate MicroPython:
//   - ESP32 / RP2040: machine.Pin + machine.PWM
//   - micro:bit: pinN.write_analog()

// --- Block definitions ---

api.Blockly.Blocks['hbridge_motor_init'] = {
  init() {
    this.appendDummyInput()
      .appendField('init motor')
      .appendField(new api.Blockly.FieldDropdown([
        ['A', 'A'],
        ['B', 'B'],
      ]), 'MOTOR')
      .appendField('IN1 pin')
      .appendField(new api.Blockly.FieldNumber(2, 0, 48, 1), 'IN1')
      .appendField('IN2 pin')
      .appendField(new api.Blockly.FieldNumber(3, 0, 48, 1), 'IN2');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Initialize an h-bridge motor channel with two pins');
  },
};

api.Blockly.Blocks['hbridge_motor_speed'] = {
  init() {
    this.appendDummyInput()
      .appendField('set motor')
      .appendField(new api.Blockly.FieldDropdown([
        ['A', 'A'],
        ['B', 'B'],
      ]), 'MOTOR');
    this.appendValueInput('SPEED')
      .setCheck('Number')
      .appendField('speed');
    this.setInputsInline(true);
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Set motor speed: -255 (full reverse) to +255 (full forward)');
  },
};

api.Blockly.Blocks['hbridge_motor_stop'] = {
  init() {
    this.appendDummyInput()
      .appendField('stop motor')
      .appendField(new api.Blockly.FieldDropdown([
        ['A', 'A'],
        ['B', 'B'],
      ]), 'MOTOR');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Stop an h-bridge motor (brake)');
  },
};

// --- Helpers used by generators ---

function isMicrobit() {
  return api.getDeviceId() === 'microbit';
}

function mbPin(pin) {
  var n = Math.max(0, Math.min(20, parseInt(pin, 10) || 0));
  return 'pin' + n;
}

// --- Code generators ---

api.pythonGenerator.forBlock['hbridge_motor_init'] = function (block) {
  var motor = block.getFieldValue('MOTOR');
  var in1 = block.getFieldValue('IN1');
  var in2 = block.getFieldValue('IN2');
  var m = motor.toLowerCase();

  if (isMicrobit()) {
    // micro:bit: no explicit init needed, just store pin numbers for later use.
    // We define variables so speed/stop blocks can reference them.
    api.pythonGenerator.definitions_['import_microbit'] = 'from microbit import *';
    api.pythonGenerator.definitions_['hb_motor_' + m + '_pins'] =
      'motor_' + m + '_in1 = ' + mbPin(in1) + '\n' +
      'motor_' + m + '_in2 = ' + mbPin(in2);
    api.pythonGenerator.definitions_['hb_set_motor_mb'] = [
      'def _hb_set_motor(p1, p2, speed):',
      '    speed = max(-255, min(255, int(speed)))',
      '    duty = abs(speed) * 4',
      '    if speed > 0:',
      '        p1.write_analog(duty)',
      '        p2.write_analog(0)',
      '    elif speed < 0:',
      '        p1.write_analog(0)',
      '        p2.write_analog(duty)',
      '    else:',
      '        p1.write_analog(0)',
      '        p2.write_analog(0)',
    ].join('\n');
  } else {
    // ESP32 / RP2040: machine.PWM (pins stored as numbers, PWM created on demand)
    api.pythonGenerator.definitions_['import_machine'] =
      'from machine import Pin, PWM';
    api.pythonGenerator.definitions_['hb_motor_' + m + '_in1'] =
      'motor_' + m + '_in1 = ' + in1;
    api.pythonGenerator.definitions_['hb_motor_' + m + '_in2'] =
      'motor_' + m + '_in2 = ' + in2;
    api.pythonGenerator.definitions_['hb_set_motor'] = [
      'def _hb_set_motor(p1, p2, speed):',
      '    speed = max(-255, min(255, int(speed)))',
      '    duty = abs(speed) * 4',
      '    if speed > 0:',
      '        try: PWM(Pin(p2)).deinit()',
      '        except: pass',
      '        Pin(p2, Pin.OUT).value(0)',
      '        PWM(Pin(p1), freq=1000, duty=duty)',
      '    elif speed < 0:',
      '        try: PWM(Pin(p1)).deinit()',
      '        except: pass',
      '        Pin(p1, Pin.OUT).value(0)',
      '        PWM(Pin(p2), freq=1000, duty=duty)',
      '    else:',
      '        try: PWM(Pin(p1)).deinit()',
      '        except: pass',
      '        try: PWM(Pin(p2)).deinit()',
      '        except: pass',
      '        Pin(p1, Pin.OUT).value(0)',
      '        Pin(p2, Pin.OUT).value(0)',
    ].join('\n');
  }
  return '';
};

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

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

// --- Dual-motor block definitions ---

api.Blockly.Blocks['hbridge_dual_init'] = {
  init() {
    this.appendDummyInput().appendField('init dual motors');
    this.appendDummyInput()
      .appendField('left IN1')
      .appendField(new api.Blockly.FieldNumber(2, 0, 48, 1), 'L_IN1')
      .appendField('IN2')
      .appendField(new api.Blockly.FieldNumber(3, 0, 48, 1), 'L_IN2');
    this.appendDummyInput()
      .appendField('right IN1')
      .appendField(new api.Blockly.FieldNumber(4, 0, 48, 1), 'R_IN1')
      .appendField('IN2')
      .appendField(new api.Blockly.FieldNumber(5, 0, 48, 1), 'R_IN2');
    this.setInputsInline(false);
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Initialize two h-bridge motors (left and right) with 4 pins.');
  },
};

api.Blockly.Blocks['hbridge_dual_speed'] = {
  init() {
    this.appendDummyInput().appendField('set motors');
    this.appendValueInput('LEFT')
      .setCheck('Number')
      .appendField('left');
    this.appendValueInput('RIGHT')
      .setCheck('Number')
      .appendField('right');
    this.setInputsInline(true);
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Set left and right motor speeds independently (-255 to 255).');
  },
};

api.Blockly.Blocks['hbridge_dual_forward'] = {
  init() {
    this.appendDummyInput().appendField('drive forward');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Drive both motors forward using the speed from "set motors".');
  },
};

api.Blockly.Blocks['hbridge_dual_backward'] = {
  init() {
    this.appendDummyInput().appendField('drive backward');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Drive both motors backward using the speed from "set motors".');
  },
};

api.Blockly.Blocks['hbridge_dual_left'] = {
  init() {
    this.appendDummyInput().appendField('turn left');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Turn left (left motor backward, right motor forward).');
  },
};

api.Blockly.Blocks['hbridge_dual_right'] = {
  init() {
    this.appendDummyInput().appendField('turn right');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Turn right (left motor forward, right motor backward).');
  },
};

api.Blockly.Blocks['hbridge_dual_stop'] = {
  init() {
    this.appendDummyInput().appendField('stop motors');
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(30);
    this.setTooltip('Stop both motors.');
  },
};

// --- Dual-motor generators ---

api.pythonGenerator.forBlock['hbridge_dual_init'] = function (block) {
  var l1 = block.getFieldValue('L_IN1');
  var l2 = block.getFieldValue('L_IN2');
  var r1 = block.getFieldValue('R_IN1');
  var r2 = block.getFieldValue('R_IN2');

  if (isMicrobit()) {
    api.pythonGenerator.definitions_['import_microbit'] = 'from microbit import *';
    api.pythonGenerator.definitions_['hb_dual_pins'] =
      'motor_l_in1 = ' + mbPin(l1) + '\n' +
      'motor_l_in2 = ' + mbPin(l2) + '\n' +
      'motor_r_in1 = ' + mbPin(r1) + '\n' +
      'motor_r_in2 = ' + mbPin(r2);
    api.pythonGenerator.definitions_['hb_set_motor_mb'] = [
      'def _hb_set_motor(p1, p2, speed):',
      '    speed = max(-255, min(255, int(speed)))',
      '    duty = abs(speed) * 4',
      '    if speed > 0:',
      '        p1.write_analog(duty)',
      '        p2.write_analog(0)',
      '    elif speed < 0:',
      '        p1.write_analog(0)',
      '        p2.write_analog(duty)',
      '    else:',
      '        p1.write_analog(0)',
      '        p2.write_analog(0)',
    ].join('\n');
  } else {
    api.pythonGenerator.definitions_['import_machine'] =
      'from machine import Pin, PWM';
    api.pythonGenerator.definitions_['hb_motor_l_in1'] = 'motor_l_in1 = ' + l1;
    api.pythonGenerator.definitions_['hb_motor_l_in2'] = 'motor_l_in2 = ' + l2;
    api.pythonGenerator.definitions_['hb_motor_r_in1'] = 'motor_r_in1 = ' + r1;
    api.pythonGenerator.definitions_['hb_motor_r_in2'] = 'motor_r_in2 = ' + r2;
    api.pythonGenerator.definitions_['hb_set_motor'] = [
      'def _hb_set_motor(p1, p2, speed):',
      '    speed = max(-255, min(255, int(speed)))',
      '    duty = abs(speed) * 4',
      '    if speed > 0:',
      '        try: PWM(Pin(p2)).deinit()',
      '        except: pass',
      '        Pin(p2, Pin.OUT).value(0)',
      '        PWM(Pin(p1), freq=1000, duty=duty)',
      '    elif speed < 0:',
      '        try: PWM(Pin(p1)).deinit()',
      '        except: pass',
      '        Pin(p1, Pin.OUT).value(0)',
      '        PWM(Pin(p2), freq=1000, duty=duty)',
      '    else:',
      '        try: PWM(Pin(p1)).deinit()',
      '        except: pass',
      '        try: PWM(Pin(p2)).deinit()',
      '        except: pass',
      '        Pin(p1, Pin.OUT).value(0)',
      '        Pin(p2, Pin.OUT).value(0)',
    ].join('\n');
  }
  return '';
};

api.pythonGenerator.forBlock['hbridge_dual_speed'] = function (block) {
  var left = api.pythonGenerator.valueToCode(
    block, 'LEFT', api.pythonGenerator.ORDER_NONE) || '0';
  var right = api.pythonGenerator.valueToCode(
    block, 'RIGHT', api.pythonGenerator.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';
};

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

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

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

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

api.pythonGenerator.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';
};

// --- Register toolbox category ---

api.registerCategories([
  {
    kind: 'category',
    name: 'H-Bridge Motor',
    colour: '30',
    contents: [
      { kind: 'block', type: 'hbridge_motor_init' },
      {
        kind: 'block',
        type: 'hbridge_motor_speed',
        inputs: {
          SPEED: { shadow: { type: 'math_number', fields: { NUM: 200 } } },
        },
      },
      { kind: 'block', type: 'hbridge_motor_stop' },
      { kind: 'block', type: 'hbridge_dual_init' },
      {
        kind: 'block',
        type: 'hbridge_dual_speed',
        inputs: {
          LEFT: { shadow: { type: 'math_number', fields: { NUM: 200 } } },
          RIGHT: { shadow: { type: 'math_number', fields: { NUM: 200 } } },
        },
      },
      { kind: 'block', type: 'hbridge_dual_forward' },
      { kind: 'block', type: 'hbridge_dual_backward' },
      { kind: 'block', type: 'hbridge_dual_left' },
      { kind: 'block', type: 'hbridge_dual_right' },
      { kind: 'block', type: 'hbridge_dual_stop' },
    ],
  },
]);
