var blocklyDiv = document.getElementById('blocklyDiv');
var blocklyArea = document.getElementById('blocklyArea');
var connectButton = document.getElementById('connectButton');
var runButton = document.getElementById('runButton');

Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
Blockly.JavaScript.addReservedWords('highlightBlock');

var receivedMsg = "";

var port;
let textEncoder = new TextEncoder();


function highlightBlock(id) {
  workspace.highlightBlock(id);
}

//Lance le code tel que rentré par le joueur
function runCode() {
	init_var();
	var code = Blockly.JavaScript.workspaceToCode(workspace); //On récupère un code javascript
	console.log(code)
	var myInterpreter = new Interpreter(code, initApi); //Intanciation d'une VM avec fonctions réécrites
	function nextStep() {
  	if (myInterpreter.step()) {
    	setTimeout(nextStep, 10);
  	}
	}
	nextStep();
  //Il est possible de lancer le code de façon séquencielle
}



/************************************
 FONCTIONS DE COMMUNICATION ARDUINO
 ************************************/

//Envoie ce string à l'Arduino
function sendString(str) {
  if (port !== undefined) {
    port.send(textEncoder.encode(str)).catch(error => {
      console.log('Send error: ' + error);
    });
  }
}

//Attend jusqu'à ce que le message "UNLOCK;" soit reçu de l'arduino
function waitForUnlockFromArduino() {
  if (receivedMsg != "UNLOCK;") {
    setTimeout(waitForUnlockFromArduino, 10);
    return;
  }
}

//Attend un message de l'Arduino et retourne ses arguments éventuels
//Les arguments sont séparés par '_'
function waitForMessageFromArduino(message) {
  var args = receivedMsg.split('_');
  if (args[0] != message) {
    setTimeout(waitForUnlockFromArduino, 10);
    return;
  }
  return args.slice(1);
}



/*******************************************************
IMPLEMENTATION DES FONCTIONS DANS L'INTERPRETEUR JS
********************************************************/

function changeVar(variable, value) {
	variable = value;
}

//Interprétation des commandes par l'utiliseur (redéfinition des fonctions avec IO)
function initApi(interpreter, scope) {
  var wrapper = function(text) {
		return enableManual();
	};
	interpreter.setProperty(scope, 'enableManual',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return changePower(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'changePower',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return isInMaxQ();
	};
	interpreter.setProperty(scope, 'isInMaxQ',
	interpreter.createNativeFunction(wrapper));
	
	var wrapper = function(text) {
		return isInOrbit();
	};
	interpreter.setProperty(scope, 'isInOrbit',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return isFuelEmpty();
	};
	interpreter.setProperty(scope, 'isFuelEmpty',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return getLanderSpeed();
	};
	interpreter.setProperty(scope, 'getLanderSpeed',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return isLanderLanded();
	};
	interpreter.setProperty(scope, 'isLanderLanded',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return changeRocketStage();
	};
	interpreter.setProperty(scope, 'changeRocketStage',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return launchRocket();
	};
	interpreter.setProperty(scope, 'launchRocket',
	interpreter.createNativeFunction(wrapper));

	var wrapper = function(text) {
		return displayConsignes(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'displayConsignes',
	interpreter.createNativeFunction(wrapper));
	// Add an API function for the changeVar() block.
	var wrapper = function(text) {
		return changeVar(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'changeVar',
	interpreter.createNativeFunction(wrapper));
	// Add an API function for the alert() block.
	var wrapper = function(text) {
		return alert(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'alert',
	interpreter.createNativeFunction(wrapper));

  // Add an API function for the sendString() block.
	wrapper = function(text) {
		return sendString(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'sendString',
	interpreter.createNativeFunction(wrapper));

  // Add an API function for the waitForUnlockFromArduino() block.
  wrapper = function() {
		return waitForUnlockFromArduino();
	};
	interpreter.setProperty(scope, 'waitForUnlockFromArduino',
	interpreter.createNativeFunction(wrapper));

  // Add an API function for the waitForMessageFromArduino() block.
  wrapper = function(text) {
		return waitForUnlockFromArduino(arguments.length ? text : '');
	};
	interpreter.setProperty(scope, 'waitForMessageFromArduino',
	interpreter.createNativeFunction(wrapper));
	// Add an API function for highlighting blocks.
  var wrapper = function(id) {
    return workspace.highlightBlock(id);
  };
  interpreter.setProperty(scope, 'highlightBlock',
      interpreter.createNativeFunction(wrapper));
}



/***************************************************
 GESTION DES CONNEXIONS ET INITIALISATION SERIAL
***************************************************/

//Instanciation de la liaison série à la fin du chargement de la page
document.addEventListener('DOMContentLoaded', event => {
	runButton.addEventListener('click', runCode);
  function connect() {
    console.log('Connecting to ' + port.device_.productName + '...');
    port.connect().then(() => {
      console.log(port);
      console.log('Connected.');
      connectButton.textContent = 'Se déconnecter';
			connectButton.style.background = 'linear-gradient(to bottom, #B52812 5%, #99220F 100%)';
			connectButton.style.border = '3px solid #801C0D';
			runButton.style.background = 'linear-gradient(to bottom, #77B55B 5%, #568241 100%)';
			runButton.style.border = '3px solid #476B36';
      port.onReceive = data => {
        let textDecoder = new TextDecoder();
        receivedMsg = textDecoder.decode(data);
        console.log("Reçu :" + receivedMsg);
      }
      port.onReceiveError = error => {
        console.log('Receive error: ' + error);
      };
    }, error => {
      console.log('Connection error: ' + error);
    });
  };

  connectButton.addEventListener('click', function() {
    if (port) {
      port.disconnect();
      connectButton.textContent = 'Connecter à la maquette';
			connectButton.style.background = 'linear-gradient(to bottom, #77B55B 5%, #568241 100%)';
			connectButton.style.border = '3px solid #476B36';
			runButton.style.background = 'linear-gradient(to bottom, #737375 5%, #5D5C5E 100%)';
			runButton.style.border = '3px solid #353436';
			runButton.removeEventListener('click', runCode);
      port = null;
    } else {
      serial.requestPort().then(selectedPort => {
        port = selectedPort;
        connect();
      }).catch(error => {
        console.log('Connection error: ' + error);
      });
    }
  });

  serial.getPorts().then(ports => {
    if (ports.length == 0) {
      console.log('No devices found.');
    } else {
      port = ports[0];
      connect();
    }
  });
});



/***********************************************
INITIALISATION DU MODULE BLOCKLY AVEC SA TOOLBOX
************************************************/

//Chargement de la toolbox
var xml = null;
var request = new XMLHttpRequest();
request.open('GET', 'toolbox.xml', false);
request.send();
if (request.readyState == 4 && request.status == 200) {
  xml = request.responseXML;
}
var toolbox = new XMLSerializer().serializeToString(xml.documentElement);

//Instancie l'espace de travail blockly
var workspace = Blockly.inject(blocklyDiv,
      {toolbox: toolbox});

/* Adapte la taille du conteneur de l'espace du travail à l'environnement
Ca a l'air dégueulasse pour faire du responsive mais c'est le truc de la doc
officielle de google xD*/
var onresize = function(e) {
	// Compute the absolute coordinates and dimensions of blocklyArea.
  var element = blocklyArea;
  var x = 0;
  var y = 0;
  do {
    x += element.offsetLeft;
    y += element.offsetTop;
    element = element.offsetParent;
  } while (element);
  // Position blocklyDiv over blocklyArea.
  blocklyDiv.style.left = x + 'px';
  blocklyDiv.style.top = y + 'px';
  blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
  blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
  Blockly.svgResize(workspace);
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(workspace);
