import Blockly, { Block, Xml } from 'blockly';
import 'blockly/javascript';
import './blockly-vex';

// Blockの読み込み
import './blocks/js-c-block';
import './generators/js-c-block';

Blockly.HSV_SATURATION = 0.8;
Blockly.HSV_VALUE = 0.85;

Blockly.JavaScript.injectId = (msg, block) => {
  const id = block.id.replace(/\$/g, '$$$$');
  return msg.replace(/%1/g, '"' + id + '"');
};

Blockly.JavaScript.STATEMENT_PREFIX = "_highlightBlock(%1, true);\n";
Blockly.JavaScript.STATEMENT_SUFFIX = "_highlightBlock(%1, false);\n";

export class BlocklyApp {
  readonly workspace: Blockly.WorkspaceSvg;

  constructor(parent: string | Element, toolbox: string) {
    this.workspace = Blockly.inject(parent, {
      toolbox: Xml.textToDom(toolbox),
      media: 'blockly-media/',
      renderer: 'zelos',
      theme: Blockly.Theme.defineTheme('ClassicHats', {
        base: Blockly.Themes.Classic,
        startHats: true,
      }),
    });
    window.addEventListener('resize', () => Blockly.svgResize(this.workspace));
    this.workspace.addChangeListener(Blockly.Events.disableOrphans);
    this.setWorkspaceText(require('./default-workspace.xml?raw'));
  }

  /**
   * Blocklyのワークスペースを文字列に変換します。
   * setWorkspaceTextを使うことで復元できます
   */
  getWorkspaceText(): string {
    return Xml.domToText(Xml.workspaceToDom(this.workspace));
  }

  /**
   * Blocklyのワークスペースを設定します
   */
  setWorkspaceText(xmlText: string) {
    this.workspace.clear();
    Xml.domToWorkspace(Xml.textToDom(xmlText), this.workspace);
  }

  getCode(): string {
    const contents = Blockly.JavaScript.workspaceToCode(this.workspace);
    const tasks = contents.split('--FUNCTION--\n');
    console.log(contents);
    let start = '';
    let join = '';
    let i = 0;
    for (const t of tasks) {
      if (!t) continue;
      const code = t.replace(/\n([^\n])/g, '\n    $1');
      start += `  std::thread th${i}([](){\n    ${code}\n  });\n`;
      join += `  th${i}.join();\n`;
      i++;
    }

    return `#include <iostream>
#include <cstdint>
#include <thread>
#include <chrono>
#include <mutex>

std::mutex __mtx;

static void _highlightBlock(const char* id, bool on)
{
  std::lock_guard<std::mutex> lk(__mtx);
  std::cout << (on? "ON:" : "OFF:") << id << std::endl;
}

static void print(const char* msg)
{
  {
    std::lock_guard<std::mutex> lk(__mtx);
    std::cout << "MSG:" << msg << std::endl;
    fflush(stdout);
  }
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

int main(int argc, char *argv[])
{
${start}
${join}
  return 0;
}
`;
  }


  setVisible(visible: boolean) {
    this.workspace.setVisible(visible);
  }

  setHighlight(id: string, on: boolean) {
    if (on) {
      const block = this.workspace.getBlockById(id);
      const root = block.getRootBlock();
      for (const b of this.workspace.highlightedBlocks_) {
        if (root == b.getRootBlock()) {
          this.workspace.highlightBlock(b.id, false);
          break;
        }
      }
    }
    this.workspace.highlightBlock(id, on);
  }

  clearHighlight() {
    this.workspace.highlightBlock();
  }
}
