import Blockly from 'blockly';
import {
  Cpp,
  ORDER_ADDITION,
  ORDER_ATOMIC,
  ORDER_DIVISION,
  ORDER_MULTIPLICATION,
  ORDER_RELATIONAL,
  ORDER_SUBTRACTION,
} from './cpp';

Cpp['flag-start'] = block => {
  const name = Cpp.getTaskName(block.getFieldValue('name'));
  return `mylib__::createTask("${name}", [](void*) {\n`;
};

Cpp['println'] = block => {
  const text = block.getFieldValue('arg');
  // エスケープ処理
  const escaped = text.replace(/\\/g, '\\\\').replace(/\n/g, '\\\n').replace(/"/g, '\\"');
  return `mylib__::println("${escaped}");\n`;
};

Cpp['delay'] = block => {
  let delay = block.getFieldValue('arg') as number;
  delay = Math.ceil(delay * 1000);
  return `mylib__::delay(${delay});\n`;
};

Cpp['move-moter'] = block => {
  const angle = block.getFieldValue('arg') as number;
  return `mylib__::moveMoter(${Math.ceil(angle)});\n`;
};
Cpp['move-moter2'] = block => {
  const angle = Cpp.valueToCode(block, 'arg', 0);
  return `mylib__::moveMoter(${angle});\n`;
};

Cpp['buzzer'] = block => {
  const arg = block.getFieldValue('arg') as string;
  return `mylib__::soundBuzzer(mylib__::${arg});\n`;
};
Cpp['buzzer-hz'] = block => {
  const arg = Math.round(block.getFieldValue('arg') as number);
  return `mylib__::soundBuzzer(${arg});\n`;
};

function defNoArg(name: string, mylib: string) {
  Cpp[name] = () => {
    return `mylib__::${mylib};\n`;
  };
}

defNoArg('capture-camera', 'printJPEG()');
defNoArg('on-led', 'setLED(true)');
defNoArg('off-led', 'setLED(false)');
defNoArg('toggle-led', 'toggleLED()');
defNoArg('print-sensor', 'printSensorData()');
defNoArg('stop-buzzer', 'stopBuzzer()');

// ------ Loop -----------
function defLoop(name: string, inputBlock: boolean) {
  Cpp[name] = block => {
    const i = Cpp.nameDB_.getDistinctName('i', Blockly.VARIABLE_CATEGORY_NAME);
    const program = Cpp.statementToCode(block, 'program');
    const arg0 = inputBlock ? Cpp.valueToCode(block, 'arg0', ORDER_RELATIONAL) : block.getFieldValue('arg0');
    const end = arg0 ? arg0 : '0';
    return `for (uint32_t ${i} = 0; ${i} < ${end}; ++${i}) {\n${program}}\n`;
  };
}
defLoop('loop-field', false);
defLoop('loop-block', true);
Cpp['for-ever'] = block => {
  const program = Cpp.statementToCode(block, 'program');
  return `while(1) {\n${program}}\n`;
};

function defBinaryOperator(name: string, op: string, order: number) {
  Cpp[name] = block => {
    const arg0 = Cpp.valueToCode(block, 'arg0', order);
    const arg1 = Cpp.valueToCode(block, 'arg1', order);
    return [`${arg0} ${op} ${arg1}`, order];
  };
}
defBinaryOperator('add', '+', ORDER_ADDITION);
defBinaryOperator('sub', '-', ORDER_SUBTRACTION);
defBinaryOperator('mul', '*', ORDER_MULTIPLICATION);
defBinaryOperator('div', '/', ORDER_DIVISION);

Cpp['number'] = block => {
  const x = block.getFieldValue('arg0');
  return [x.toString(), ORDER_ATOMIC];
};

function defIfLeGt(name: string, lt: boolean) {
  Cpp[name] = block => {
    const program = Cpp.statementToCode(block, 'program');
    const arg0 = Cpp.valueToCode(block, 'arg0', ORDER_RELATIONAL);
    const arg1 = block.getFieldValue('arg1');
    const ltgt = lt ? '<' : '>';
    return `if (${arg0} ${ltgt} ${arg1}) {\n${program}}\n`;
  };
}

defIfLeGt('if-le', true);
defIfLeGt('if-gt', false);
