/**
 * Statechart compiler by Glenn De Jonghe
 * Javascript generator by Joeri Exelmans
 * 
 * Date:   Fri Apr 08 11:44:57 2016
 * 
 * Model author: Simon Van Mierlo
 * Model name:   DebuggingEnvironment
 * Model description:
    Debugging environment for Dynamic Structure DEVS in HTML + JS (d3/w2ui).
 */

// put everything in an object (serves as "namespace")
DebuggingEnvironment = {};

// closure scope
(function() {

// The actual constructor
var MainApp = function(controller) {
    MainApp.prototype.commonConstructor.call(this,controller);
    
    // constructor body (user-defined)
};


MainApp.prototype = new RuntimeClassBase();

// Unique IDs for all statechart nodes
MainApp.prototype.Root = 0;
MainApp.prototype.Root_creating_toolbar = 1;
MainApp.prototype.Root_creating_visualizer = 2;
MainApp.prototype.Root_running = 3;

// Statechart enter/exit action method(s) :

MainApp.prototype.enter_Root_creating_toolbar = function() {
    this.object_manager.addEvent(new Event("create_instance", null, [this, 'toolbar','DebuggingToolbar']));
    this.current_state[this.Root].push(this.Root_creating_toolbar);
};

MainApp.prototype.exit_Root_creating_toolbar = function() {
    this.current_state[this.Root] = new Array();
};

MainApp.prototype.enter_Root_creating_visualizer = function() {
    this.object_manager.addEvent(new Event("create_instance", null, [this, 'visualizer','Visualizer']));
    this.current_state[this.Root].push(this.Root_creating_visualizer);
};

MainApp.prototype.exit_Root_creating_visualizer = function() {
    this.current_state[this.Root] = new Array();
};

MainApp.prototype.enter_Root_running = function() {
    this.current_state[this.Root].push(this.Root_running);
};

MainApp.prototype.exit_Root_running = function() {
    this.current_state[this.Root] = new Array();
};

// Statechart transitions :

MainApp.prototype.transition_Root = function(event) {
    var catched = false;
    if (!catched) {
        if (this.current_state[this.Root][0] === this.Root_creating_toolbar) {
            catched = this.transition_Root_creating_toolbar(event);
        }
        else if (this.current_state[this.Root][0] === this.Root_creating_visualizer) {
            catched = this.transition_Root_creating_visualizer(event);
        }
        else if (this.current_state[this.Root][0] === this.Root_running) {
            catched = this.transition_Root_running(event);
        }
    }
    return catched;
};

MainApp.prototype.transition_Root_creating_toolbar = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "instance_created") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_creating_toolbar. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var association_name = parameters[0];
            this.exit_Root_creating_toolbar();
            this.object_manager.addEvent(new Event("start_instance", null, [this, association_name]));
            this.enter_Root_creating_visualizer();
        }
        catched = true;
    }
    
    return catched;
};

MainApp.prototype.transition_Root_creating_visualizer = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "instance_created") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_creating_visualizer. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var association_name = parameters[0];
            this.exit_Root_creating_visualizer();
            this.object_manager.addEvent(new Event("start_instance", null, [this, association_name]));
            this.enter_Root_running();
        }
        catched = true;
    }
    
    return catched;
};

MainApp.prototype.transition_Root_running = function(event) {
    var catched = false;
    return catched;
};

// Execute transitions
MainApp.prototype.transition = function(event) {
    if (!event) event = new Event();
    this.state_changed = this.transition_Root(event);
};

// inState method for statechart
MainApp.prototype.inState = function(nodes) {
    for (var c in this.current_state) {
        if (!this.current_state.hasOwnProperty(c)) continue;
        var new_nodes = new Array();
        for (var n in nodes) {
            if (!nodes.hasOwnProperty(n)) continue;
            if (this.current_state[c].indexOf(nodes[n]) === -1) {
                new_nodes.push(nodes[n]);
            }
        }
        nodes = new_nodes;
        if (nodes.length === 0) {
            return true;
        }
    }
    return false;
};

MainApp.prototype.commonConstructor = function(controller) {
    if (!controller) controller = null;
    // Constructor part that is common for all constructors.
    RuntimeClassBase.call(this);
    
    // User defined input ports
    this.inports = new Object();
    this.controller = controller;
    this.object_manager = (controller == null ? null : controller.object_manager);
    this.current_state = new Object();
    this.history_state = new Object();
    
    // Initialize statechart
    this.current_state[this.Root] = new Array();
};

MainApp.prototype.start = function() {
    RuntimeClassBase.prototype.start.call(this);
    this.enter_Root_creating_toolbar();
};

// put class in global diagram object
DebuggingEnvironment.MainApp = MainApp;

// The actual constructor
var DebuggingToolbar = function(controller) {
    DebuggingToolbar.prototype.commonConstructor.call(this,controller);
    
    // constructor body (user-defined)
    this.parameters = {};
    this.buttons = {};
    this.curr_parameter = null;
    this.button_counter = 0;
    this.buttons_to_create = [{parent: this, description: 'Simulate', icon: 'simulate', command: 'simulate'},
                              {parent: this, description: 'Realtime Simulate', icon: 'simulatert', command: 'realtime', parameter_needed: 'realtime_scale'},
                              {parent: this, description: 'Realtime Scale', html: 'input size="1" value="1.0" style="border: 1px solid silver"', default: 1.0, parameter: 'realtime_scale'},
                              {parent: this, description: 'Pause', icon: 'pause', command: 'pause'},
                              {parent: this, description: 'Big Step', icon: 'bigstep', command: 'big_step'},
                              {parent: this, description: 'Small Step', icon: 'smallstep', command: 'small_step'},
                              {parent: this, description: 'Reset', icon: 'reset', command: 'reset'},
                              {parent: this, description: 'Add Breakpoint', icon: 'breakpoint', action: function() {breakpoint_dialog.dialog( "open" );}}];
    this.tb = ui.create_toolbar('DebuggingToolbar');
};


DebuggingToolbar.prototype = new RuntimeClassBase();

// Unique IDs for all statechart nodes
DebuggingToolbar.prototype.Root = 0;
DebuggingToolbar.prototype.Root_initializing = 1;
DebuggingToolbar.prototype.Root_initializing_check = 2;
DebuggingToolbar.prototype.Root_initializing_creating_button = 3;
DebuggingToolbar.prototype.Root_initializing_running = 4;
DebuggingToolbar.prototype.Root_initializing_getting_parameter = 5;

// Statechart enter/exit action method(s) :

DebuggingToolbar.prototype.enter_Root_initializing = function() {
    this.current_state[this.Root].push(this.Root_initializing);
};

DebuggingToolbar.prototype.exit_Root_initializing = function() {
    if (this.current_state[this.Root_initializing].indexOf(this.Root_initializing_check) !== -1) {
        this.exit_Root_initializing_check();
    }
    if (this.current_state[this.Root_initializing].indexOf(this.Root_initializing_creating_button) !== -1) {
        this.exit_Root_initializing_creating_button();
    }
    if (this.current_state[this.Root_initializing].indexOf(this.Root_initializing_running) !== -1) {
        this.exit_Root_initializing_running();
    }
    if (this.current_state[this.Root_initializing].indexOf(this.Root_initializing_getting_parameter) !== -1) {
        this.exit_Root_initializing_getting_parameter();
    }
    this.current_state[this.Root] = new Array();
};

DebuggingToolbar.prototype.enter_Root_initializing_check = function() {
    this.current_state[this.Root_initializing].push(this.Root_initializing_check);
};

DebuggingToolbar.prototype.exit_Root_initializing_check = function() {
    this.current_state[this.Root_initializing] = new Array();
};

DebuggingToolbar.prototype.enter_Root_initializing_creating_button = function() {
    this.current_state[this.Root_initializing].push(this.Root_initializing_creating_button);
};

DebuggingToolbar.prototype.exit_Root_initializing_creating_button = function() {
    this.current_state[this.Root_initializing] = new Array();
};

DebuggingToolbar.prototype.enter_Root_initializing_running = function() {
    this.current_state[this.Root_initializing].push(this.Root_initializing_running);
};

DebuggingToolbar.prototype.exit_Root_initializing_running = function() {
    this.current_state[this.Root_initializing] = new Array();
};

DebuggingToolbar.prototype.enter_Root_initializing_getting_parameter = function() {
    this.current_state[this.Root_initializing].push(this.Root_initializing_getting_parameter);
};

DebuggingToolbar.prototype.exit_Root_initializing_getting_parameter = function() {
    this.current_state[this.Root_initializing] = new Array();
};

// Statechart enter/exit default method(s) :

DebuggingToolbar.prototype.enterDefault_Root_initializing = function() {
    this.enter_Root_initializing();
    this.enter_Root_initializing_check();
};

// Statechart transitions :

DebuggingToolbar.prototype.transition_Root = function(event) {
    var catched = false;
    if (!catched) {
        if (this.current_state[this.Root][0] === this.Root_initializing) {
            catched = this.transition_Root_initializing(event);
        }
    }
    return catched;
};

DebuggingToolbar.prototype.transition_Root_initializing = function(event) {
    var catched = false;
    if (!catched) {
        if (this.current_state[this.Root_initializing][0] === this.Root_initializing_check) {
            catched = this.transition_Root_initializing_check(event);
        }
        else if (this.current_state[this.Root_initializing][0] === this.Root_initializing_creating_button) {
            catched = this.transition_Root_initializing_creating_button(event);
        }
        else if (this.current_state[this.Root_initializing][0] === this.Root_initializing_running) {
            catched = this.transition_Root_initializing_running(event);
        }
        else if (this.current_state[this.Root_initializing][0] === this.Root_initializing_getting_parameter) {
            catched = this.transition_Root_initializing_getting_parameter(event);
        }
    }
    return catched;
};

DebuggingToolbar.prototype.transition_Root_initializing_check = function(event) {
    var catched = false;
    var enableds = new Array();
    if (this.buttons_to_create.length > 0) {
        enableds.push(1);
    }
    
    if (this.buttons_to_create.length == 0) {
        enableds.push(2);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_initializing_check. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            this.exit_Root_initializing_check();
            if ('parameter' in this.buttons_to_create[0]) {
                this.curr_parameter = this.buttons_to_create[0]['parameter']
            }
            this.object_manager.addEvent(new Event("create_instance", null, [this, 'buttons','Button','button' + this.button_counter,this.tb,this.buttons_to_create.shift()]));
            this.enter_Root_initializing_creating_button();
        }
        else if (enabled === 2) {
            this.exit_Root_initializing_check();
            this.enter_Root_initializing_running();
        }
        catched = true;
    }
    
    return catched;
};

DebuggingToolbar.prototype.transition_Root_initializing_creating_button = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "instance_created") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_initializing_creating_button. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var association_name = parameters[0];
            this.exit_Root_initializing_creating_button();
            this.object_manager.addEvent(new Event("start_instance", null, [this, association_name]));
            if (this.curr_parameter) {
                this.parameters[this.curr_parameter] = association_name;
                this.curr_parameter = null;
            }
            this.buttons['button' + this.button_counter] = association_name;
            this.button_counter += 1;
            this.enter_Root_initializing_check();
        }
        catched = true;
    }
    
    return catched;
};

DebuggingToolbar.prototype.transition_Root_initializing_running = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "get_parameter") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_initializing_running. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var the_parameter = parameters[0];
            
            var respond_id = parameters[1];
            this.exit_Root_initializing_running();
            var send_event = new Event("get_parameter", null, []);
            this.object_manager.addEvent(new Event("narrow_cast", null, [this, this.parameters[the_parameter] , send_event]));
            this.respond_id = respond_id;
            this.enter_Root_initializing_getting_parameter();
        }
        catched = true;
    }
    
    return catched;
};

DebuggingToolbar.prototype.transition_Root_initializing_getting_parameter = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "parameter_reply") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_initializing_getting_parameter. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var parameter_value = parameters[0];
            this.exit_Root_initializing_getting_parameter();
            var send_event = new Event("parameter_reply", null, [parameter_value]);
            this.object_manager.addEvent(new Event("narrow_cast", null, [this, this.buttons[this.respond_id] , send_event]));
            this.enter_Root_initializing_running();
        }
        catched = true;
    }
    
    return catched;
};

// Execute transitions
DebuggingToolbar.prototype.transition = function(event) {
    if (!event) event = new Event();
    this.state_changed = this.transition_Root(event);
};

// inState method for statechart
DebuggingToolbar.prototype.inState = function(nodes) {
    for (var c in this.current_state) {
        if (!this.current_state.hasOwnProperty(c)) continue;
        var new_nodes = new Array();
        for (var n in nodes) {
            if (!nodes.hasOwnProperty(n)) continue;
            if (this.current_state[c].indexOf(nodes[n]) === -1) {
                new_nodes.push(nodes[n]);
            }
        }
        nodes = new_nodes;
        if (nodes.length === 0) {
            return true;
        }
    }
    return false;
};

DebuggingToolbar.prototype.commonConstructor = function(controller) {
    if (!controller) controller = null;
    // Constructor part that is common for all constructors.
    RuntimeClassBase.call(this);
    
    // User defined input ports
    this.inports = new Object();
    this.controller = controller;
    this.object_manager = (controller == null ? null : controller.object_manager);
    this.current_state = new Object();
    this.history_state = new Object();
    
    // Initialize statechart
    this.current_state[this.Root] = new Array();
    this.current_state[this.Root_initializing] = new Array();
};

DebuggingToolbar.prototype.start = function() {
    RuntimeClassBase.prototype.start.call(this);
    this.enterDefault_Root_initializing();
};

// put class in global diagram object
DebuggingEnvironment.DebuggingToolbar = DebuggingToolbar;

// The actual constructor
var Button = function(controller, button_id, toolbar, parameters) {
    Button.prototype.commonConstructor.call(this,controller);
    
    // constructor body (user-defined)
    this.button_id = button_id;
    this.toolbar = toolbar;
    this.action = null;
    this.command = null;
    if ('parameter_needed' in parameters) {
        this.parameter_needed = parameters['parameter_needed'];
    } else {
        this.parameter_needed = null;
    }
    if ('html' in parameters) {
        var the_html = parameters['html']
        the_html += ' id="' + this.button_id + '_input" onchange="ui.parameter_changed(\'' + button_id + '\');"'
        this.btn = ui.create_tb_element(this.button_id, '<' + the_html + ' />', parameters['default'], this.toolbar, this.controller, this.inports['button_ui_input']);
    } else if ('action' in parameters) {
        this.action = parameters['action'];
        this.btn = ui.create_button(this.button_id, parameters['description'], parameters['icon'], this.toolbar, this.controller, this.inports['button_ui_input']);
    } else {
        this.command = parameters['command'];
        this.btn = ui.create_button(this.button_id, parameters['description'], parameters['icon'], this.toolbar, this.controller, this.inports['button_ui_input']);
    }
};


Button.prototype = new RuntimeClassBase();

// Unique IDs for all statechart nodes
Button.prototype.Root = 0;
Button.prototype.Root_listening = 1;
Button.prototype.Root_getting_parameter = 2;

// Statechart enter/exit action method(s) :

Button.prototype.enter_Root_listening = function() {
    this.current_state[this.Root].push(this.Root_listening);
};

Button.prototype.exit_Root_listening = function() {
    this.current_state[this.Root] = new Array();
};

Button.prototype.enter_Root_getting_parameter = function() {
    this.current_state[this.Root].push(this.Root_getting_parameter);
};

Button.prototype.exit_Root_getting_parameter = function() {
    this.current_state[this.Root] = new Array();
};

// Statechart transitions :

Button.prototype.transition_Root = function(event) {
    var catched = false;
    if (!catched) {
        if (this.current_state[this.Root][0] === this.Root_listening) {
            catched = this.transition_Root_listening(event);
        }
        else if (this.current_state[this.Root][0] === this.Root_getting_parameter) {
            catched = this.transition_Root_getting_parameter(event);
        }
    }
    return catched;
};

Button.prototype.transition_Root_listening = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "mouse_press" && event.port === "button_ui_input") {
        if (this.action) {
            enableds.push(1);
        }
    }
    
    if (event.name === "mouse_press" && event.port === "button_ui_input") {
        if (this.parameter_needed == null && this.command) {
            enableds.push(2);
        }
    }
    
    if (event.name === "mouse_press" && event.port === "button_ui_input") {
        if (this.parameter_needed != null && this.command) {
            enableds.push(3);
        }
    }
    
    if (event.name === "get_parameter") {
        enableds.push(4);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_listening. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            this.exit_Root_listening();
            this.action();
            this.enter_Root_listening();
        }
        else if (enabled === 2) {
            this.exit_Root_listening();
            this.controller.outputEvent(new Event("debugging_command", "output", [this.command]));
            this.enter_Root_listening();
        }
        else if (enabled === 3) {
            this.exit_Root_listening();
            var send_event = new Event("get_parameter", null, [this.parameter_needed,this.button_id]);
            this.object_manager.addEvent(new Event("narrow_cast", null, [this, 'parent' , send_event]));
            this.enter_Root_getting_parameter();
        }
        else if (enabled === 4) {
            this.exit_Root_listening();
            var send_event = new Event("parameter_reply", null, [this.btn.value]);
            this.object_manager.addEvent(new Event("narrow_cast", null, [this, 'parent' , send_event]));
            this.enter_Root_listening();
        }
        catched = true;
    }
    
    return catched;
};

Button.prototype.transition_Root_getting_parameter = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "parameter_reply") {
        enableds.push(1);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_getting_parameter. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var the_parameter = parameters[0];
            this.exit_Root_getting_parameter();
            var the_parameters = {};
            the_parameters[this.parameter_needed] = the_parameter;
            this.controller.outputEvent(new Event("debugging_command", "output", [this.command,the_parameters]));
            this.enter_Root_listening();
        }
        catched = true;
    }
    
    return catched;
};

// Execute transitions
Button.prototype.transition = function(event) {
    if (!event) event = new Event();
    this.state_changed = this.transition_Root(event);
};

// inState method for statechart
Button.prototype.inState = function(nodes) {
    for (var c in this.current_state) {
        if (!this.current_state.hasOwnProperty(c)) continue;
        var new_nodes = new Array();
        for (var n in nodes) {
            if (!nodes.hasOwnProperty(n)) continue;
            if (this.current_state[c].indexOf(nodes[n]) === -1) {
                new_nodes.push(nodes[n]);
            }
        }
        nodes = new_nodes;
        if (nodes.length === 0) {
            return true;
        }
    }
    return false;
};

Button.prototype.commonConstructor = function(controller) {
    if (!controller) controller = null;
    // Constructor part that is common for all constructors.
    RuntimeClassBase.call(this);
    
    // User defined input ports
    this.inports = new Object();
    this.inports["button_ui_input"] = controller.addInputPort("button_ui_input", this);
    this.controller = controller;
    this.object_manager = (controller == null ? null : controller.object_manager);
    this.current_state = new Object();
    this.history_state = new Object();
    
    // Initialize statechart
    this.current_state[this.Root] = new Array();
};

Button.prototype.start = function() {
    RuntimeClassBase.prototype.start.call(this);
    this.enter_Root_listening();
};

// put class in global diagram object
DebuggingEnvironment.Button = Button;

// The actual constructor
var Visualizer = function(controller) {
    Visualizer.prototype.commonConstructor.call(this,controller);
    
    // constructor body (user-defined)
    the_controller = this.controller;
    this.simulation_info = d3.select("body").append("div")
                                            .attr("class", "simulationInfo")
                                            .style("margin", "5px")
                                            .style("display", "block");
    this.simulation_time = this.simulation_info.append("span")
                                               .append("text")
                                               .text("SIMULATION TIME: (not started)")
                                               .style("float", "left");
    this.simulation_steps = this.simulation_info.append("span")
                                                .style("margin", "5px")
                                                .style("float", "left")
                                                .attr("class", "invisibleInfo")
                                                .attr("id", "smallStepInfo");
    this.simulation_steps.selectAll("div").data([0,1,2,3,4,5,6,7])
                                          .enter()
                                            .append("div")
                                            .attr("id", function(d) {return "smallStepInfo_" + d})
                                            .attr("class", "smallStepInfo inactive")
                                            .style("transform", function(d) {return "translate(" + d * 5 + "px, -4px) rotate(45deg)"});
    d3.select("body").append("br");
    this.breakpoint_info = d3.select("body").append("div")
                                            .attr("class", "breakpointInfo")
                                            .style("margin", "5px")
                                            .style("display", "block");
    this.breakpoint_text = this.breakpoint_info.append("span")
                                               .append("text")
                                               .text("BREAKPOINTS: ")
                                               .style("float", "left");
    this.breakpoint_infos = this.breakpoint_info.append("span")
                                                .style("margin", "5px")
                                                .style("float", "left")
                                                .attr("id", "breakpointInfos");
    this.breakpoints = [];
    this.data = [];
    this.port_connections = [];
    this.loc_to_item = {};
    event_to_inject = undefined;
    god_event_to_process = undefined;
    
    // TODO: this is domain-specific!
    this.particle_id_to_item = {};
    
    var margin = {top: 50, right: 0, bottom: 0, left: 0},
        width = 960 - margin.right - margin.left,
        height = 1024 - margin.top - margin.bottom;
    
    this.node_id = 0;
    this.tree = d3.layout.tree()
        .size([width, height]);
    this.diagonal = d3.svg.diagonal();
    this.port_connection_diagonal = d3.svg.diagonal().projection(function(d) {return [d.x, d.y]});
    this.parent_svg = d3.select("body").append("svg")
        .attr("width", width + margin.right + margin.left)
        .attr("height", height + margin.top + margin.bottom);
    
    var defs = this.parent_svg.append('svg:defs')
    
    var markers = [{name: 'arrow', path: 'M2,2 L2,11 L10,6 L2,2', fill: "red"},
                   {name: 'arrow_green', path: 'M2,2 L2,11 L10,6 L2,2', fill: "green"}];
    var marker = defs.selectAll('marker')
                     .data(markers)
                     .enter()
                     .append('svg:marker')
                       .attr('id', function(d){ return 'marker_' + d.name})
                       .attr('markerHeight', 13)
                       .attr('markerWidth', 13)
                       .attr('markerUnits', 'strokeWidth')
                       .attr('orient', 'auto')
                       .attr('refX', 2)
                       .attr('refY', 6)
                       .append('svg:path')
                         .attr('d', function(d){ return d.path })
                         .attr('fill', function(d){ return d.fill });
                         
    this.svg = this.parent_svg.append("g")
                              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    function godEvent() {
        god_event_to_process = {'model': $('#currLoc').text(), 'attribute': $('#godEventName').val(), 'value': eval($('#godEventValue').val())};
        the_controller.addInput(new Event("process_god_event", "ui_input", [god_event_to_process]), 0.0);
        dialog.dialog("close");
    }
    
    dialog = $( "#godEventPopup" ).dialog({
      autoOpen: false,
      height: 200,
      width: 325,
      modal: true,
      buttons: {
        "Submit": godEvent,
        Cancel: function() {
          dialog.dialog( "close" );
        }
      },
      close: function() {
        form[ 0 ].reset();
      }
    }).css("font-size", "14px");;
    
    form = dialog.find( "form" ).on( "submit", function( event ) {
      event.preventDefault();
      godEvent();
    });
    
    function injectEvent() {
        event_to_inject = {'port': $('#currLoc').text(), 'event': eval($('#injectEvent').val()), 'time': eval($('#injectTime').val())};
        the_controller.addInput(new Event("inject_event", "ui_input", [event_to_inject]), 0.0);
        inject_dialog.dialog("close");
    }
    
    inject_dialog = $( "#injectEventPopup" ).dialog({
      autoOpen: false,
      height: 200,
      width: 325,
      modal: true,
      buttons: {
        "Submit": injectEvent,
        Cancel: function() {
          inject_dialog.dialog( "close" );
        }
      },
      close: function() {
        inject_form[ 0 ].reset();
      }
    }).css("font-size", "14px");
    
    inject_form = inject_dialog.find( "form" ).on( "submit", function( event ) {
      event.preventDefault();
      injectEvent();
    });
    
    function addBreakpoint() {
        breakpoint_to_add = [$('#breakpointName').val(), $('#breakpointFunction').val(), $('#breakpointEnabled').is(":checked"), $('#breakpointDisableOnTrigger').is(":checked")];
        the_controller.addInput(new Event("add_breakpoint", "ui_input", [breakpoint_to_add]), 0.0);
        breakpoint_dialog.dialog("close");
    }
    
    breakpoint_dialog = $( "#breakpointPopup" ).dialog({
      autoOpen: false,
      height: 400,
      width: 600,
      modal: true,
      buttons: {
        "Submit": addBreakpoint,
        Cancel: function() {
          breakpoint_dialog.dialog( "close" );
        }
      },
      close: function() {
        breakpoint_form[ 0 ].reset();
      }
    }).css("font-size", "14px");
    
    breakpoint_form = breakpoint_dialog.find( "form" ).on( "submit", function( event ) {
      event.preventDefault();
      addBreakpoint();
    });
};


Visualizer.prototype = new RuntimeClassBase();

// Unique IDs for all statechart nodes
Visualizer.prototype.Root = 0;
Visualizer.prototype.Root_initializing = 1;
Visualizer.prototype.Root_running = 2;

// User defined method
Visualizer.prototype.update_vis = function(structural_changes) {
    d3.select("#smallStepInfo_7").classed("inactive", false);
    var created_models = [],
        created_ports = [],
        connected_ports = [],
        deleted_models = [],
        deleted_ports = [],
        disconnected_ports = [];
        
    if ('CREATED_MODELS' in structural_changes) {
        created_models = structural_changes['CREATED_MODELS'];
    }
    if ('CREATED_PORTS' in structural_changes) {
        created_ports = structural_changes['CREATED_PORTS'];
    }
    if ('CONNECTED_PORTS' in structural_changes) {
        connected_ports = structural_changes['CONNECTED_PORTS'];
    }
    if ('DELETED_MODELS' in structural_changes) {
        deleted_models = structural_changes['DELETED_MODELS'];
    }
    if ('DELETED_PORTS' in structural_changes) {
        deleted_ports = structural_changes['DELETED_PORTS'];
    }
    if ('DISCONNECTED_PORTS' in structural_changes) {
        disconnected_ports = structural_changes['DISCONNECTED_PORTS'];
    }
    
    // sort according to their depth (amounts of dots in their name)
    created_models.sort(function(a, b) {
        return (a.match(/./g) || []).length - (b.match(/./g) || []).length
    })
                    
    data = this.data;
                    
    var node_id = this.node_id;
    var loc_to_item = this.loc_to_item;
    created_models.forEach(function(item) {
        splitted_path = item.split('.');
        element_to_insert = data;
        while (splitted_path.length > 1) {
            curr_name = splitted_path.shift();
            element_to_insert = element_to_insert.find(function(el) {return el['name'] == curr_name}).children;
        }
        var node = {'name': splitted_path[0], 'children': [], 'ports': [], 'loc': item, 'id': node_id++}
        element_to_insert.push(node);
        loc_to_item[item] = node;
    });
    
    created_ports.forEach(function(item) {
        splitted_path = item[0].split('.');
        curr_array = data;
        while (splitted_path.length > 2) {
            curr_name = splitted_path.shift();
            curr_array = curr_array.find(function(el) {return el['name'] == curr_name}).children;
        }
        ports = curr_array.find(function(el) {return el['name'] == splitted_path[0]})['ports'];
        var node = {'name': splitted_path[1], 'is_input': item[1], 'loc': item[0], 'id': node_id++};
        ports.push(node);
        loc_to_item[item[0]] = node;
    });
    this.node_id = node_id;
    
    port_connections = this.port_connections
    connected_ports.forEach(function(item) {
        splitted_path_src = item[0].split('.');
        curr_array = data;
        while (splitted_path_src.length > 2) {
            curr_name = splitted_path_src.shift();
            curr_array = curr_array.find(function(el) {return el['name'] == curr_name}).children;
        }
        src_parent = curr_array.find(function(el) {return el['name'] == splitted_path_src[0]});
        src = src_parent['ports'].find(function(el) {return el['name'] == splitted_path_src[1]});
        splitted_path_dst = item[1].split('.');
        curr_array = data;
        while (splitted_path_dst.length > 2) {
            curr_name = splitted_path_dst.shift();
            curr_array = curr_array.find(function(el) {return el['name'] == curr_name}).children;
        }
        dst_parent = curr_array.find(function(el) {return el['name'] == splitted_path_dst[0]});
        dst = dst_parent['ports'].find(function(el) {return el['name'] == splitted_path_dst[1]});
        port_connections.push({'source': src, 'target': dst});
    });
    
    disconnected_ports.forEach(function(item) {
        port_connections = port_connections.filter(function(el) {return el.source.loc != item[0] || el.target.loc != item[1];});
    });
    this.port_connections = port_connections
    
    deleted_ports.forEach(function(item) {
        splitted_path = item.split('.');
        curr_array = data;
        while (splitted_path.length > 2) {
            curr_name = splitted_path.shift();
            curr_array = curr_array.find(function(el) {return el['name'] == curr_name}).children;
        }
        parent = curr_array.find(function(el) {return el['name'] == splitted_path[0]});
        parent['ports'] = parent['ports'].filter(function(el) {return el['name'] != splitted_path[1]});
    });
    
    deleted_models.forEach(function(item) {
        splitted_path = item.split('.');
        curr_array = data;
        while (splitted_path.length > 2) {
            curr_name = splitted_path.shift();
            curr_array = element_to_insert.find(function(el) {return el['name'] == curr_name}).children;
        }
        parent = curr_array.find(function(el) {return el['name'] == splitted_path[0]});
        parent['children'] = parent['children'].filter(function(el) {return el['name'] != splitted_path[1]});
    });
    
    this.data = data;
    
    var nodes = this.tree.nodes(this.data[0]),
        links = this.tree.links(nodes);
    nodes.forEach(function(d, i) {
                    d.y = d.depth * 100 + i * 40
                    d.ports.forEach(function(p, i) {p.dx = 13, p.dy = 18; p.x = (d.x - 40) + i * p.dx; p.y = d.y + p.dy;});
                  });
    
    // Declare the nodes...
    var node = this.svg.selectAll("g.node")
                       .data(nodes, function(d) { return d.id; });
                  
    // Enter the nodes.
    var nodeEnter = node.enter().append("g")
                                .attr("class", "node")
                                .attr("id", function(d) {return 'instance_' + d.id;})
                                .attr("transform", function(d) { 
                                                        return "translate(" + d.x + "," + d.y + ")";
                                                   });
    nodeEnter.append("rect")
             .attr("width", 120)
             .attr("height", 25)
             .attr("transform", "translate(-60, 0)")
             .on("click", function(d) {
                console.log(JSON.stringify(d.state, null, 2));
             })
             .on("mouseover", function(d) {
                                var node = d3.select("#instance_" + d.id)
                                var stateInfo = node.selectAll(".stateInfo")
                                                    .data(function(n) {if (n.state) {return [n.state]} else {return []}}, function(d) {return d.id});
                                var stateInfoEnter = stateInfo.enter().append("text")
                                                                      .attr("transform", "translate(-60, 60)")
                                                                      .attr("id", this.node_id++)
                                                                      .attr("class", "stateInfo");
                                var stateInfoText = stateInfo.selectAll("tspan")
                                                             .data(function(d) {return JSON.stringify(d, null, 2).split("\n")});
                                var stateInfoTextEnter = stateInfoText.enter().append("tspan")
                                                                              .attr("x", "0.0")
                                                                              .attr("dy", "1.4em")
                                                                              .attr("xml:space", "preserve")
                                                                              .text(function(d) {return d});
                             })
             .on("mouseout", function(d) {
                                var node = d3.select("#instance_" + d.id)
                                var stateInfo = node.selectAll(".stateInfo");
                                stateInfo.selectAll("tspan").data([]).exit().remove();
                             })
             .on("contextmenu", function (d, i) {
                $( "#currLoc" ).html(d.loc);
                d3.event.preventDefault();
                if (!d.children) {
                    dialog.dialog( "open" );
                }
             });
    nodeEnter.append("text")
             .attr("y", 12)
             .attr("dy", ".35em")
             .attr("text-anchor", "middle")
             .text(function(d) { return d.name; })
             .style("fill-opacity", 1);
    nodeEnter.append("text")
             .attr("class", "timeNext")
             .attr("y", -10)
             .attr("x", 30)
             .attr("dy", ".35em")
             .attr("text-anchor", "middle")
             .text("TN: --")
             .style("fill-opacity", 1);
             
    port = node.selectAll("g.port")
               .data(function(n) {return n.ports}, function(d) { return d.id; })
    
    portEnter = port.enter().append("g")
                            .attr("class", "port invisible")
                            .attr("id", function(d) {return 'port_' + d.id;})
                            .on("mouseover", function(d) {
                                                d3.selectAll(".source_" + d.id).classed("invisibleLink", false)
                                                                               .classed("input", false);
                                                d3.selectAll(".target_" + d.id).classed("invisibleLink", false)
                                                                               .classed("input", true);
                                                port_connections.forEach(function(port_connection) {
                                                    if (port_connection.source.id == d.id) {
                                                        d3.select('#port_' + port_connection.target.id).attr("class", "port visible");
                                                    } else if (port_connection.target.id == d.id) {
                                                        d3.select('#port_' + port_connection.source.id).attr("class", "port visible");
                                                    }
                                                });
                                                d3.select(this).attr("class", "port visible");
                                             })
                            .on("mouseout", function(d) {
                                                d3.selectAll(".source_" + d.id).classed("invisibleLink", true);
                                                d3.selectAll(".target_" + d.id).classed("invisibleLink", true);
                                                d3.selectAll(".port").attr("class", "port invisible");
                                            })
                            .on("click", function (d, i) {
                                $('#currLoc').html(d.loc);
                                d3.event.preventDefault();
                                if (d.is_input) {
                                    inject_dialog.dialog( "open" );
                                }
                            });
    portEnter.append("rect")
             .attr("width", 10)
             .attr("height", 10)
             .style("fill", function(d) {return d.is_input ? "green" : "purple";})
             .attr("transform", "rotate(45)");
    portEnter.append("text")
             .attr("dy", ".35em")
             .attr("text-anchor", "left")
             .text(function(d) {return d.name;})
             .attr("transform", "translate(0, 20) rotate(30)");
    
    port.attr("transform", function(d, i) {return "translate(" + (-40 + i * d.dx) + ", " + d.dy + ")"});
                      
    // Declare the links...
    var link = this.svg.selectAll("path.link")
                       .data(links);
      
    // Enter the links.
    link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", this.diagonal);
    
    // Declare port connections...
    var port_connection = this.svg.selectAll("path.portConnection")
                       .data(port_connections);
    
    // Enter port connections.
    port_connection.enter().insert("path", "g")
        .attr("class", "invisibleLink portConnection");
    
    // Update the nodes.
    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    node.classed("coupled", function(d) {return d.children});
    
    // Transition links to their new position.
    link.attr("d", this.diagonal);
    
    // Transition links to their new position.
    port_connection.attr("d", function(d) {
                                var dx = d.target.x - d.source.x,
                                    dy = d.target.y - d.source.y,
                                    dr = Math.sqrt(dx * dx + dy * dy)/4,
                                    mx = d.source.x + dx/2,
                                    my = d.source.y + dy/2 + 30;
                                return [
                                  "M",d.source.x,d.source.y+5,
                                  "T",mx,my,
                                  "T",d.target.x,d.target.y+5
                                ].join(" ");
                              })
                   .attr("class", function(d) {return "invisibleLink portConnection source_" + d.source.id + " target_" + d.target.id});
    
    // Remove nodes and links.
    node.exit().remove();
    link.exit().remove();
    port.exit().remove();
    port_connection.exit().remove();
    
};
// User defined method
Visualizer.prototype.visualize_initial_state = function(initial_states) {
    d3.select("#smallStepInfo").classed("invisibleInfo", true);
    d3.selectAll("g.node").attr("class", "node")
                          .classed("coupled", function(d) {return d.children});
    d3.selectAll("circle.inmessage").data([]).exit().remove();
    d3.selectAll("circle.outmessage").data([]).exit().remove();
    d3.selectAll(".portConnection").classed("invisibleLink", true);
    d3.selectAll(".port").classed("visible", false);
    d3.selectAll(".port").classed("invisible", true);
    d3.selectAll(".stateInfo").data([]).exit().remove();
    
    for (var loc in initial_states) {
        var item = this.loc_to_item[loc];
        d3.select("#instance_" + item.id + " .timeNext").text("TN: " + initial_states[loc][0][0] + "/" + initial_states[loc][0][1])
        item.state = initial_states[loc][1];
        if (item.state.particle_id) {
            this.particle_id_to_item[item.state.particle_id] = item; // TODO: this is somewhat ugly, we're making a lot of assumptions here and it's no longer domain-independent!
        }
    }
    
};
// User defined method
Visualizer.prototype.visualize_new_state = function(new_states, new_tn) {
    d3.select("#smallStepInfo").classed("invisibleInfo", true);
    d3.selectAll("g.node").attr("class", "node")
                          .classed("coupled", function(d) {return d.children});
    d3.selectAll("circle.inmessage").data([]).exit().remove();
    d3.selectAll("circle.outmessage").data([]).exit().remove();
    d3.selectAll(".portConnection").classed("invisibleLink", true);
    d3.selectAll(".port").classed("visible", false);
    d3.selectAll(".port").classed("invisible", true);
    d3.selectAll(".stateInfo").data([]).exit().remove();
    
    for (var loc in new_tn) {
        var item = this.loc_to_item[loc];
        d3.select("#instance_" + item.id + " .timeNext").text("TN: " + new_tn[loc][0] + "/" + new_tn[loc][1])
        item.state = new_states[loc];
        if (item.state.particle_id != undefined) {
            this.particle_id_to_item[item.state.particle_id] = item; // TODO: this is somewhat ugly, we're making a lot of assumptions here and it's no longer domain-independent!
        }
    }
    
};
// User defined method
Visualizer.prototype.visualize_imminents = function(imminents) {
    d3.selectAll(".portConnection").classed("invisibleLink", true);
    d3.selectAll(".port").classed("visible", false);
    d3.selectAll(".port").classed("invisible", true);
    d3.selectAll("circle.inmessage").data([]).exit().remove();
    d3.selectAll("circle.outmessage").data([]).exit().remove();
    if (this.transitioning_nodes) {
        this.transitioning_nodes.forEach(function(n) {n.attr("class", "node")
                                                       .classed("coupled", function(d) {return d.children});});
    }
    
    d3.select("#smallStepInfo").classed("invisibleInfo", false);
    d3.selectAll(".smallStepInfo").classed("inactive", true);
    d3.select("#smallStepInfo_0").classed("inactive", false);
    var loc_to_item = this.loc_to_item;
    this.imminents = imminents;
    imminents.forEach(function(imminent) {
                        d3.select("#instance_" + loc_to_item[imminent].id).classed("imminent", true);
                     })
    
};
// User defined method
Visualizer.prototype.visualize_selected_imminent = function(selected_imminent) {
    d3.select("#smallStepInfo_1").classed("inactive", false);
    var loc_to_item = this.loc_to_item;
    this.imminents.forEach(function(imminent) {
                             if (!(imminent == selected_imminent)) {
                                d3.select("#instance_" + loc_to_item[imminent].id).classed("imminent", false);
                             }
                          })
    
};
// User defined method
Visualizer.prototype.visualize_messages = function(bag, msg_type) {
    var total_msgs = [];
    for (var key in bag) {
        var msgs = bag[key];
        var port = this.loc_to_item[key];
        if (port != undefined) {
            msgs[0].port = port;
            Array.prototype.push.apply(total_msgs, msgs);
        }
    }
    var msgs_vis = this.svg.selectAll("circle." + msg_type)
                           .data(total_msgs);
    var msg_enter = msgs_vis.enter().append("circle")
                                    .attr("class", msg_type)
                                    .attr("id", function(m) {return "msg_" + m.port.id;})
                                    .attr("r", 5)
                                    .attr("transform", function(m) {return "translate(" + m.port.x + "," + (m.port.y + 22) + ")"})
                                    .on("click", function(d) {console.log(d[0])});
    
};
// User defined method
Visualizer.prototype.visualize_inbags = function(inbags) {
    d3.select("#smallStepInfo_3").classed("inactive", false);
    function transition(circle, path, source_port_vis, target_port_vis) {
        circle.transition()
              .duration(3000)
              .attrTween("transform", translateAlong(path.node()))
              .each("end", function() {
                                path.classed("invisibleLink", true);
                                source_port_vis.classed("invisible", true);
                                target_port_vis.classed("invisible", true);
                                source_port_vis.classed("visible", false);
                                target_port_vis.classed("visible", false);
                                circle.attr("class", "inmessage");
                            });
    }
    
    // Returns an attrTween for translating along the specified path element.
    function translateAlong(path) {
      var l = path.getTotalLength();
      return function(d, i, a) {
        return function(t) {
          var p = path.getPointAtLength(t * l);
          return "translate(" + p.x + "," + (p.y + 17) + ")";
        };
      };
    }
    var outports_visited = []
    for (var key in inbags) {
        if (inbags[key].length == 2) {
            var msgs = inbags[key][1];
            var inport = this.loc_to_item[key];
            if (inport != undefined) {
                var circle = undefined;
                var outport_id = this.loc_to_item[inbags[key][0]].id;
                if (outports_visited.indexOf(outport_id) > -1) {
                    circle = d3.select('#msg_' + outport_id);
                    var parent = d3.select(circle.node().parentNode);
                    circle = parent.append("circle")
                                   .attr("class", "outmessage")
                                   .attr("id", circle.id + outports_visited.indexOf(outport_id))
                                   .attr("r", 5)
                                   .attr("transform", circle.transform)
                                   .on("click", (function(msgs) {return function(d) {console.log(msgs[0])}})(msgs));
                } else {
                    circle = d3.select('#msg_' + outport_id);
                    circle.on("click", (function(msgs) {return function(d) {console.log(msgs[0])}})(msgs));
                }
                var path = d3.select(".source_" + outport_id + ".target_" + inport.id);
                var source_port_vis = d3.select("#port_" + outport_id)
                var target_port_vis = d3.select("#port_" + inport.id)
                source_port_vis.classed("invisible", false);
                target_port_vis.classed("invisible", false);
                source_port_vis.classed("visible", true);
                target_port_vis.classed("visible", true);
                path.classed("invisibleLink", false);
                transition(circle, path, source_port_vis, target_port_vis);
                outports_visited.push(outport_id);
            }
        } else {
            var msgs = inbags[key][0];
            var inport = this.loc_to_item[key];
            this.svg.append("circle")                        
                    .attr("class", "inmessage")
                    .attr("id", "msg_" + inport.id)
                    .attr("r", 5)
                    .attr("transform", "translate(" + inport.x + "," + (inport.y + 22) + ")")
                    .on("click", (function(msgs) {return function(d) {console.log(msgs[0])}})(msgs));
        }
    }
    
};
// User defined method
Visualizer.prototype.visualize_transitioning = function(transitioning) {
    var transitioning_nodes = [];
    var loc_to_item = this.loc_to_item;
    Object.keys(transitioning).forEach(function(key) {
                                            transitioning_nodes.push(d3.select("#instance_" + loc_to_item[key].id).classed(transitioning[key], true));
                                         });
    this.transitioning_nodes = transitioning_nodes;
    
};
// User defined method
Visualizer.prototype.handle_internal_state_change = function(new_internal_states) {
    d3.select("#smallStepInfo_5").classed("inactive", false);
    for (var loc in new_internal_states) {
        var item = this.loc_to_item[loc];
        item.state = new_internal_states[loc];
    }
    if (this.transitioning_nodes) {
        this.transitioning_nodes.forEach(function(n) {n.attr("class", "node")
                                                       .classed("coupled", function(d) {return d.children});});
    }
             
    var node = this.svg.selectAll("g.node");
    var stateInfo = node.selectAll("g.state");
    stateInfo.text(function(d) {return JSON.stringify(d);});
    
};
// User defined method
Visualizer.prototype.visualize_new_tn = function(new_tn) {
    d3.select("#smallStepInfo_6").classed("inactive", false);
    for (var loc in new_tn) {
        var item = this.loc_to_item[loc];
        d3.select("#instance_" + item.id + " .timeNext").text("TN: " + new_tn[loc][0] + "/" + new_tn[loc][1])
    }
    
};
// User defined method
Visualizer.prototype.select_instance = function(instance_id) {
    d3.selectAll(".stateInfo").data([]).exit().remove();
    if (instance_id in this.particle_id_to_item) {
        var d = this.particle_id_to_item[instance_id];
        var node = d3.select("#instance_" + d.id)
        var stateInfo = node.selectAll(".stateInfo")
                            .data(function(n) {if (n.state) {return [n.state]} else {return []}}, function(d) {return d.id});
        var stateInfoEnter = stateInfo.enter().append("text")
                                              .attr("transform", "translate(-60, 60)")
                                              .attr("id", this.node_id++)
                                              .attr("class", "stateInfo");
        var stateInfoText = stateInfo.selectAll("tspan")
                                     .data(function(d) {return JSON.stringify(d, null, 2).split("\n")});
        var stateInfoTextEnter = stateInfoText.enter().append("tspan")
                                                      .attr("x", "0.0")
                                                      .attr("dy", "1.4em")
                                                      .attr("xml:space", "preserve")
                                                      .text(function(d) {return d});
    }
    
};
// User defined method
Visualizer.prototype.visualize_breakpoints = function() {
    for (idx in this.breakpoints) {
        var curr_bp = this.breakpoints[idx];
        if (curr_bp.triggered) {
            this.visualize_messages(curr_bp.outbag, 'outmessage');
            for (var key in curr_bp.inbags) {
                if (curr_bp.inbags[key].length == 2) {
                    curr_bp.inbags[key] = [curr_bp.inbags[key][1]];
                }
            }
            this.visualize_messages(curr_bp.inbags, 'inmessage');
            this.visualize_transitioning(curr_bp.transitioned);
        }
    }
    
    var curr_bps = this.breakpoint_infos.selectAll("div").data(this.breakpoints)
    
    curr_bps_enter = curr_bps.enter().append("div")                
                                     .attr("id", function(d) {return "breakpoint_" + d.name})
                                     .attr("class", "breakpoint")
                                     .style("transform", function(d, i) {return "translate(" + i * 5 + "px, -8px)"})
                                     .style("float", "left");
    curr_bps_enter.append("text")
                  .text(function(d) {return d.name})
                  .on("click", function(d) {
                    d.enabled = !d.enabled;
                    the_controller.addInput(new Event("toggle_breakpoint", "ui_input", [d.name, d.enabled]), 0.0);
                  })
                  .on('mouseover', function() {
                    d3.event.preventDefault();
                  });
    curr_bps_enter.append("img")
                  .attr('src', 'img/delete-bp.png')
                  .style('transform', 'translate(1px, 2px)')
                  .on("click", function(d) {
                    the_controller.addInput(new Event("del_breakpoint", "ui_input", [d.name]), 0.0);
                  });
    curr_bps.exit().remove();
    curr_bps.style("background-color", function(d) {
                                        if (d.triggered) {
                                            d.triggered = false;
                                            if (d.disable_on_trigger) {
                                                d.enabled = false;
                                            }
                                            return "red";
                                        } else {
                                            return "transparent";
                                        }});
    curr_bps.select("text").style("opacity", function(d) {if (d.enabled) return "1"; else return "0.5";});
    
};
// Statechart enter/exit action method(s) :

Visualizer.prototype.enter_Root_initializing = function() {
    this.current_state[this.Root].push(this.Root_initializing);
};

Visualizer.prototype.exit_Root_initializing = function() {
    this.current_state[this.Root] = new Array();
};

Visualizer.prototype.enter_Root_running = function() {
    this.current_state[this.Root].push(this.Root_running);
};

Visualizer.prototype.exit_Root_running = function() {
    this.current_state[this.Root] = new Array();
};

// Statechart transitions :

Visualizer.prototype.transition_Root = function(event) {
    var catched = false;
    if (!catched) {
        if (this.current_state[this.Root][0] === this.Root_initializing) {
            catched = this.transition_Root_initializing(event);
        }
        else if (this.current_state[this.Root][0] === this.Root_running) {
            catched = this.transition_Root_running(event);
        }
    }
    return catched;
};

Visualizer.prototype.transition_Root_initializing = function(event) {
    var catched = false;
    var enableds = new Array();
    enableds.push(1);
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_initializing. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            this.exit_Root_initializing();
            this.controller.outputEvent(new Event("reset", "output", []));
            this.enter_Root_running();
        }
        catched = true;
    }
    
    return catched;
};

Visualizer.prototype.transition_Root_running = function(event) {
    var catched = false;
    var enableds = new Array();
    if (event.name === "all_states_reset" && event.port === "simulation_input") {
        enableds.push(1);
    }
    
    if (event.name === "all_states" && event.port === "simulation_input") {
        enableds.push(2);
    }
    
    if (event.name === "new_states" && event.port === "simulation_input") {
        enableds.push(3);
    }
    
    if (event.name === "imminents" && event.port === "simulation_input") {
        enableds.push(4);
    }
    
    if (event.name === "selected_imminent" && event.port === "simulation_input") {
        enableds.push(5);
    }
    
    if (event.name === "outbag" && event.port === "simulation_input") {
        enableds.push(6);
    }
    
    if (event.name === "inbags" && event.port === "simulation_input") {
        enableds.push(7);
    }
    
    if (event.name === "transitioning" && event.port === "simulation_input") {
        enableds.push(8);
    }
    
    if (event.name === "new_internal_states" && event.port === "simulation_input") {
        enableds.push(9);
    }
    
    if (event.name === "new_tn" && event.port === "simulation_input") {
        enableds.push(10);
    }
    
    if (event.name === "structural_changes" && event.port === "simulation_input") {
        enableds.push(11);
    }
    
    if (event.name === "select_instance" && event.port === "ui_input") {
        enableds.push(12);
    }
    
    if (event.name === "process_god_event" && event.port === "ui_input") {
        enableds.push(13);
    }
    
    if (event.name === "god_event_ok" && event.port === "simulation_input") {
        enableds.push(14);
    }
    
    if (event.name === "inject_event" && event.port === "ui_input") {
        enableds.push(15);
    }
    
    if (event.name === "inject_ok" && event.port === "simulation_input") {
        enableds.push(16);
    }
    
    if (event.name === "add_breakpoint" && event.port === "ui_input") {
        enableds.push(17);
    }
    
    if (event.name === "toggle_breakpoint" && event.port === "ui_input") {
        enableds.push(18);
    }
    
    if (event.name === "del_breakpoint" && event.port === "ui_input") {
        enableds.push(19);
    }
    
    if (event.name === "breakpoint_triggered" && event.port === "simulation_input") {
        enableds.push(20);
    }
    
    if (enableds.length > 1) {
        console.log("Runtime warning : indeterminism detected in a transition from node Root_running. Only the first in document order enabled transition is executed.")
    }
    
    if (enableds.length > 0) {
        var enabled = enableds[0];
        if (enabled === 1) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.data = [];
            this.port_connections = [];
            this.update_vis(the_parameters[2]);
            this.visualize_initial_state(the_parameters[1]);
            this.breakpoints = the_parameters[3];
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 2) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.update_vis(the_parameters[2]);
            this.visualize_initial_state(the_parameters[1]);
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 3) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.update_vis(the_parameters[3]);
            this.visualize_new_state(the_parameters[1], the_parameters[2]);
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 4) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.visualize_breakpoints();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.visualize_imminents(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 5) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.visualize_selected_imminent(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 6) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            d3.select("#smallStepInfo_2").classed("inactive", false);
            d3.selectAll("g.node").classed("imminent", false);
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.visualize_messages(the_parameters[1], 'outmessage');
            this.enter_Root_running();
        }
        else if (enabled === 7) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.visualize_inbags(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 8) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            d3.select("#smallStepInfo_4").classed("inactive", false);
            d3.selectAll(".portConnection").classed("invisibleLink", true);
            d3.selectAll(".port").classed("visible", false);
            d3.selectAll(".port").classed("invisible", true);
            d3.selectAll("circle.inmessage").data([]).exit().remove();
            d3.selectAll("circle.outmessage").data([]).exit().remove();
            this.visualize_transitioning(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 9) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.handle_internal_state_change(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 10) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.visualize_new_tn(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 11) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.simulation_time.text("SIMULATION TIME: " + the_parameters[0][0] + "/" + the_parameters[0][1]);
            this.update_vis(the_parameters[1]);
            this.enter_Root_running();
        }
        else if (enabled === 12) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.select_instance(the_parameters);
            this.enter_Root_running();
        }
        else if (enabled === 13) {
            this.exit_Root_running();
            this.controller.outputEvent(new Event("god_event", "output", [god_event_to_process]));
            this.enter_Root_running();
        }
        else if (enabled === 14) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            this.handle_internal_state_change(the_parameters[0]);
            this.enter_Root_running();
        }
        else if (enabled === 15) {
            this.exit_Root_running();
            this.controller.outputEvent(new Event("inject", "output", [event_to_inject]));
            this.enter_Root_running();
        }
        else if (enabled === 16) {
            this.exit_Root_running();
            this.enter_Root_running();
        }
        else if (enabled === 17) {
            this.exit_Root_running();
            this.controller.outputEvent(new Event("add_breakpoint", "output", [breakpoint_to_add]));
            this.breakpoints.push({'name': breakpoint_to_add[0], 'enabled': breakpoint_to_add[2], 'disable_on_trigger': breakpoint_to_add[3], 'triggered': false});
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 18) {
            var parameters = event.parameters;
            
            var breakpoint_id = parameters[0];
            
            var enabled = parameters[1];
            this.exit_Root_running();
            this.controller.outputEvent(new Event("toggle_breakpoint", "output", [breakpoint_id,enabled]));
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 19) {
            var parameters = event.parameters;
            
            var breakpoint_id = parameters[0];
            this.exit_Root_running();
            this.controller.outputEvent(new Event("del_breakpoint", "output", [breakpoint_id]));
            this.breakpoints = this.breakpoints.filter(function(bp) {return bp.name != breakpoint_id});
            this.visualize_breakpoints();
            this.enter_Root_running();
        }
        else if (enabled === 20) {
            var parameters = event.parameters;
            
            var the_parameters = parameters[0];
            this.exit_Root_running();
            for (var idx in this.breakpoints) {
                var curr_bp = this.breakpoints[idx];
                if (curr_bp.name == the_parameters[0]) {
                    curr_bp.triggered = true;
                    curr_bp.transitioned = the_parameters[1];
                    curr_bp.outbag = the_parameters[2];
                    curr_bp.inbags = the_parameters[3];
                }
            }
            this.enter_Root_running();
        }
        catched = true;
    }
    
    return catched;
};

// Execute transitions
Visualizer.prototype.transition = function(event) {
    if (!event) event = new Event();
    this.state_changed = this.transition_Root(event);
};

// inState method for statechart
Visualizer.prototype.inState = function(nodes) {
    for (var c in this.current_state) {
        if (!this.current_state.hasOwnProperty(c)) continue;
        var new_nodes = new Array();
        for (var n in nodes) {
            if (!nodes.hasOwnProperty(n)) continue;
            if (this.current_state[c].indexOf(nodes[n]) === -1) {
                new_nodes.push(nodes[n]);
            }
        }
        nodes = new_nodes;
        if (nodes.length === 0) {
            return true;
        }
    }
    return false;
};

Visualizer.prototype.commonConstructor = function(controller) {
    if (!controller) controller = null;
    // Constructor part that is common for all constructors.
    RuntimeClassBase.call(this);
    
    // User defined input ports
    this.inports = new Object();
    this.controller = controller;
    this.object_manager = (controller == null ? null : controller.object_manager);
    this.current_state = new Object();
    this.history_state = new Object();
    
    // Initialize statechart
    this.current_state[this.Root] = new Array();
};

Visualizer.prototype.start = function() {
    RuntimeClassBase.prototype.start.call(this);
    this.enter_Root_initializing();
};

// put class in global diagram object
DebuggingEnvironment.Visualizer = Visualizer;

var ObjectManager = function(controller) {
    ObjectManagerBase.call(this, controller);
};

ObjectManager.prototype = new ObjectManagerBase();

ObjectManager.prototype.instantiate = function(class_name, construct_params) {
    if (class_name === "MainApp") {
        var instance = new MainApp(this.controller);
        instance.associations = new Object();
        instance.associations["toolbar"] = new Association("DebuggingToolbar", 1, 1);
        instance.associations["visualizer"] = new Association("Visualizer", 1, 1);
    } else if (class_name === "DebuggingToolbar") {
        var instance = new DebuggingToolbar(this.controller);
        instance.associations = new Object();
        instance.associations["parent"] = new Association("MainApp", 1, 1);
        instance.associations["buttons"] = new Association("Button", 0, -1);
    } else if (class_name === "Button") {
        var instance = new Button(this.controller, construct_params[0], construct_params[1], construct_params[2]);
        instance.associations = new Object();
        instance.associations["parent"] = new Association("DebuggingToolbar", 1, 1);
    } else if (class_name === "Visualizer") {
        var instance = new Visualizer(this.controller);
        instance.associations = new Object();
        instance.associations["parent"] = new Association("MainApp", 1, 1);
    }
    return instance;
};

// put in global diagram object
DebuggingEnvironment.ObjectManager = ObjectManager;

var Controller = function(keep_running, finished_callback) {
    if (keep_running === undefined) keep_running = true;
    JsEventLoopControllerBase.call(this, new ObjectManager(this), keep_running, finished_callback);
    this.addInputPort("ui_input");
    this.addInputPort("simulation_input");
    this.addOutputPort("output");
    this.object_manager.createInstance("MainApp", []);
};

Controller.prototype = new JsEventLoopControllerBase();

// put in global diagram object
DebuggingEnvironment.Controller = Controller;

})();
