/**
 * @module whynot
 */
define(
	[
		'./Scheduler'
	],
	function(
		Scheduler
		) {
		'use strict';

		/**
		 * A virtual machine to execute whynot programs.
		 *
		 * @class VM
		 * @constructor
		 *
		 * @param {Array} program The program to run, as created by the Assembler
		 */
		function VM(program) {
			this._program = program;
			this._scheduler = new Scheduler(2, program.length);
		}

		/**
		 * Resets the VM for a new
		 * @return {[type]} [description]
		 */
		VM.prototype.reset = function() {
			this.scheduler.reset();

			// Initialize initial thread
			this.scheduler.addThread(0, [], 0, 0);
		};

		/**
		 * Executes the program in the VM with the given input stream.
		 *
		 * @method execute
		 *
		 * @param {Function} input Should return the next input item when called,
		 *                           or null when no further input is available.
		 *
		 * @return {Trace[]} All Traces which lead to acceptance of the input stream 
		 */
		VM.prototype.execute = function(input) {
			var scheduler = this._scheduler,
				program = this._program;

			// Reset the scheduler
			scheduler.reset();

			// Add initial thread
			scheduler.addThread(0, 0);

			var acceptingTraces = [],
				inputIndex = -1,
				inputItem;
			do {
				// Get next thread to execute
				var thread = scheduler.getNextThread();
				if (!thread) {
					break;
				}

				// Read next input item
				++inputIndex;
				inputItem = input();

				while (thread) {
					var instruction = program[thread.pc];

					switch (instruction.op) {
						case 'accept':
							// Only accept if we reached the end of the input
							if (!inputItem) {
								acceptingTraces.push(thread.trace);
							}
							break;

						case 'fail':
							// Branch is forbidden, end the thread
							break;

						case 'bad':
							// Continue at next pc with added badness
							scheduler.addThread(
								0,
								thread.pc + 1,
								thread,
								thread.badness + instruction.data);
							break;

						case 'test':
							// Fail if out of input
							if (!inputItem) {
								break;
							}
							// Fail if input does not match
							if (!instruction.func(inputItem, instruction.data)) {
								break;
							}
							// Continue in next generation, preserving badness
							scheduler.addThread(
								1,
								thread.pc + 1,
								thread,
								thread.badness);
							break;

						case 'jump':
							// Spawn new threads for all targets
							for (var iTarget = 0, nTargets = instruction.data.length; iTarget < nTargets; ++iTarget) {
								scheduler.addThread(
									0,
									instruction.data[iTarget],
									thread,
									thread.badness);
							}
							break;

						case 'record':
							// Invoke record callback
							var record = instruction.func(instruction.data, inputIndex);
							if (record) {
								thread.trace.records.push(record);
							}
							// Continue with next instruction
							scheduler.addThread(
								0,
								thread.pc + 1,
								thread,
								thread.badness);
							break;
					}

					// Next thread
					thread = scheduler.getNextThread();
				}

				// Continue with next generation
				// TODO: this should compact the traces in the current Generation
				scheduler.nextGeneration();
			} while (inputItem);

			return acceptingTraces;
		};

		return VM;
	}
);

@@ -395,229 +395,8 @@
 %09%7D%0A%0A
-%09%09/**%0A%09%09 * Resets the VM for a new%0A%09%09 * @return %7B%5Btype%5D%7D %5Bdescription%5D%0A%09%09 */%0A%09%09VM.prototype.reset = function() %7B%0A%09%09%09this.scheduler.reset();%0A%0A%09%09%09// Initialize initial thread%0A%09%09%09this.scheduler.addThread(0, %5B%5D, 0, 0);%0A%09%09%7D;%0A%0A
 %09%09/*

Remove obsolete VM#reset method.