let draw = SVG().addTo('#forth').size('100%', '100%')

let stack_element = (container, text) => {
  let group = container.group()
  group.add(
    container.rect()
      .size(100, 25)
      .stroke('#000').fill('#ddd')
      .attr('stroke-width', 2));
  group.add(container.text(text).dmove((65 - text.length) / 2, -2));
  console.log(group);
  return group;
}

let the_code = [
  [ 'push', 2 ],
  [ 'push', 3 ],
  [ 'push', 4 ],
  [ 'mul' ],
  [ 'add' ]
]

let the_stack = [], pc = 0, final = false;
let stack_container = draw.nested().move(draw.width() - '10%', 0)
let code_node = document.getElementById('code');

let push_val = (int) => {
  let the_element =
    stack_element(stack_container, int.toString()).move(10, 0);
  the_element.animate(100, 0, 'now').move(10, 10);
  the_stack.forEach(elem => elem.svg.animate(100, 0, 'now').dy(25));
  the_stack.push({ svg: the_element, val: int });
}

let pop_val = () => {
  let item = the_stack.pop()
  item.svg.remove();
  the_stack.forEach(elem => elem.svg.dy(-25));
  return item.val;
}

let render_code = (code, pc) => {
  while (code_node.firstChild) {
    code_node.removeChild(code_node.firstChild);
  }
  let list = document.createElement('ul');
  list.style = 'list-style-type: none;';
  code.forEach((instruction, idx) => {
    let i_type = instruction[0];
    let li = document.createElement('li');

    if (idx == pc) {
      let cursor = document.createElement('span')
      cursor.innerText = '> ';
      cursor.classList.add('instruction-cursor');
      li.appendChild(cursor);
    }

    let type_field = document.createElement('span');
    type_field.innerText = i_type;
    type_field.classList.add('instruction');
    li.appendChild(type_field);
    for (let i = 1; i < instruction.length; i++) {
      li.append(' ');
      let operand_field = document.createElement('span');
      operand_field.innerText = instruction[i];
      operand_field.classList.add('operand');
      li.appendChild(operand_field);
    }
    list.appendChild(li);
  });
  code_node.appendChild(list);
};

let reset = () => {
  the_stack.forEach(e => e.svg.remove());
  the_stack = [];
  pc = 0;
  final = false;
  document.getElementById('step').disabled = false;
  render_code(the_code, 0);
}

let step = () => {
  if (!final) {
    const insn = the_code[pc++];
    switch (insn[0]) {
      case 'push':
        push_val(insn[1]);
        break;
      case 'add':
        if (the_stack.length < 2) {
          console.error("machine error");
          document.getElementById('step').disabled = true;
        } else {
          let x = pop_val(), y = pop_val();
          push_val(x + y);
        }
        break;
      case 'mul':
        if (the_stack.length < 2) {
          console.error("machine error");
          document.getElementById('step').disabled = true;
        } else {
          let x = pop_val(), y = pop_val();
          push_val(x * y);
        }
        break;
    }
  }
  render_code(the_code, pc);
  if (pc >= the_code.length) {
    console.log("final state");
    document.getElementById('step').disabled = true;
    final = true;
  }
}

render_code(the_code, pc);
