var loadProject = function () {
    var id = location.hash.substring(1);
    if (id.length < 1 || !isFinite(id)) {
        id = '119615668';
    }
    var url = 'https://projects.scratch.mit.edu/internalapi/project/' +
        id + '/get/';
    var r = new XMLHttpRequest();
    r.onreadystatechange = function() {
        if (this.readyState === 4) {
            if (r.status === 200) {
                window.vm.loadProject(this.responseText);
            }
        }
    };
    r.open('GET', url);
    r.send();
};

window.onload = function() {
    // Lots of global variables to make debugging easier
    // Instantiate the VM.
    var vm = new window.VirtualMachine();
    window.vm = vm;

    // Loading projects from the server.
    document.getElementById('projectLoadButton').onclick = function () {
        document.location = '#' + document.getElementById('projectId').value;
        location.reload();
    };
    loadProject();

    // Instantiate the renderer and connect it to the VM.
    var canvas = document.getElementById('scratch-stage');
    var renderer = new window.RenderWebGL(canvas);
    window.renderer = renderer;
    vm.attachRenderer(renderer);
    var audioEngine = new window.AudioEngine();
    vm.attachAudioEngine(audioEngine);

    // Instantiate scratch-blocks and attach it to the DOM.
    var workspace = window.Blockly.inject('blocks', {
        media: './media/',
        zoom: {
            controls: true,
            wheel: true,
            startScale: 0.75
        },
        colours: {
            workspace: '#334771',
            flyout: '#283856',
            scrollbar: '#24324D',
            scrollbarHover: '#0C111A',
            insertionMarker: '#FFFFFF',
            insertionMarkerOpacity: 0.3,
            fieldShadow: 'rgba(255, 255, 255, 0.3)',
            dragShadowOpacity: 0.6
        }
    });
    window.workspace = workspace;

    // Attach scratch-blocks events to VM.
    workspace.addChangeListener(vm.blockListener);
    var flyoutWorkspace = workspace.getFlyout().getWorkspace();
    flyoutWorkspace.addChangeListener(vm.flyoutBlockListener);

    // Create FPS counter.
    var stats = new window.Stats();
    document.getElementById('tab-renderexplorer').appendChild(stats.dom);
    stats.dom.style.position = 'relative';
    stats.begin();

    // Playground data tabs.
    // Block representation tab.
    var blockexplorer = document.getElementById('blockexplorer');
    var updateBlockExplorer = function(blocks) {
        blockexplorer.innerHTML = JSON.stringify(blocks, null, 2);
        window.hljs.highlightBlock(blockexplorer);
    };

    // Thread representation tab.
    var threadexplorer = document.getElementById('threadexplorer');
    var cachedThreadJSON = '';
    var updateThreadExplorer = function (newJSON) {
        if (newJSON != cachedThreadJSON) {
            cachedThreadJSON = newJSON;
            threadexplorer.innerHTML = cachedThreadJSON;
            window.hljs.highlightBlock(threadexplorer);
        }
    };

    // Only request data from the VM thread if the appropriate tab is open.
    window.exploreTabOpen = false;
    var getPlaygroundData = function () {
        vm.getPlaygroundData();
        if (window.exploreTabOpen) {
            window.requestAnimationFrame(getPlaygroundData);
        }
    };

    // VM handlers.
    // Receipt of new playground data (thread, block representations).
    vm.on('playgroundData', function(data) {
        updateThreadExplorer(data.threads);
        updateBlockExplorer(data.blocks);
    });

    // Receipt of new block XML for the selected target.
    vm.on('workspaceUpdate', function (data) {
        workspace.clear();
        var dom = window.Blockly.Xml.textToDom(data.xml);
        window.Blockly.Xml.domToWorkspace(dom, workspace);
    });

    // Receipt of new list of targets, selected target update.
    var selectedTarget = document.getElementById('selectedTarget');
    vm.on('targetsUpdate', function (data) {
        // Clear select box.
        while (selectedTarget.firstChild) {
            selectedTarget.removeChild(selectedTarget.firstChild);
        }
        // Generate new select box.
        for (var i = 0; i < data.targetList.length; i++) {
            var targetOption = document.createElement('option');
            targetOption.setAttribute('value', data.targetList[i].id);
            // If target id matches editingTarget id, select it.
            if (data.targetList[i].id == data.editingTarget) {
                targetOption.setAttribute('selected', 'selected');
            }
            targetOption.appendChild(
                document.createTextNode(data.targetList[i].name)
            );
            selectedTarget.appendChild(targetOption);
        }
    });
    selectedTarget.onchange = function () {
        vm.setEditingTarget(this.value);
    };

    // Feedback for stacks and blocks running.
    vm.on('SCRIPT_GLOW_ON', function(data) {
        workspace.glowStack(data.id, true);
    });
    vm.on('SCRIPT_GLOW_OFF', function(data) {
        workspace.glowStack(data.id, false);
    });
    vm.on('BLOCK_GLOW_ON', function(data) {
        workspace.glowBlock(data.id, true);
    });
    vm.on('BLOCK_GLOW_OFF', function(data) {
        workspace.glowBlock(data.id, false);
    });
    vm.on('VISUAL_REPORT', function(data) {
        workspace.reportValue(data.id, data.value);
    });

    vm.on('SPRITE_INFO_REPORT', function(data) {
        if (data.id !== selectedTarget.value) return; // Not the editingTarget
        document.getElementById('sinfo-x').value = data.x;
        document.getElementById('sinfo-y').value = data.y;
        document.getElementById('sinfo-direction').value = data.direction;
        document.getElementById('sinfo-rotationstyle').value = data.rotationStyle;
        document.getElementById('sinfo-visible').value = data.visible;
    });

    document.getElementById('sinfo-post').addEventListener('click', function () {
        var data = {};
        data.x = document.getElementById('sinfo-x').value;
        data.y = document.getElementById('sinfo-y').value;
        data.direction = document.getElementById('sinfo-direction').value;
        data.rotationStyle = document.getElementById('sinfo-rotationstyle').value;
        data.visible = document.getElementById('sinfo-visible').value === 'true';
        vm.postSpriteInfo(data);
    });

    // Feed mouse events as VM I/O events.
    document.addEventListener('mousemove', function (e) {
        var rect = canvas.getBoundingClientRect();
        var coordinates = {
            x: e.clientX - rect.left,
            y: e.clientY - rect.top,
            canvasWidth: rect.width,
            canvasHeight: rect.height
        };
        window.vm.postIOData('mouse', coordinates);
    });
    canvas.addEventListener('mousedown', function (e) {
        var rect = canvas.getBoundingClientRect();
        var data = {
            isDown: true,
            x: e.clientX - rect.left,
            y: e.clientY - rect.top,
            canvasWidth: rect.width,
            canvasHeight: rect.height
        };
        window.vm.postIOData('mouse', data);
        e.preventDefault();
    });
    canvas.addEventListener('mouseup', function (e) {
        var rect = canvas.getBoundingClientRect();
        var data = {
            isDown: false,
            x: e.clientX - rect.left,
            y: e.clientY - rect.top,
            canvasWidth: rect.width,
            canvasHeight: rect.height
        };
        window.vm.postIOData('mouse', data);
        e.preventDefault();
    });

    // Feed keyboard events as VM I/O events.
    document.addEventListener('keydown', function (e) {
        // Don't capture keys intended for Blockly inputs.
        if (e.target != document && e.target != document.body) {
            return;
        }
        window.vm.postIOData('keyboard', {
            keyCode: e.keyCode,
            isDown: true
        });
        e.preventDefault();
    });
    document.addEventListener('keyup', function(e) {
        // Always capture up events,
        // even those that have switched to other targets.
        window.vm.postIOData('keyboard', {
            keyCode: e.keyCode,
            isDown: false
        });
        // E.g., prevent scroll.
        if (e.target != document && e.target != document.body) {
            e.preventDefault();
        }
    });

    // Run threads
    vm.start();

    // Inform VM of animation frames.
    var animate = function() {
        stats.update();
        requestAnimationFrame(animate);
    };
    requestAnimationFrame(animate);

    // Handlers for green flag and stop all.
    document.getElementById('greenflag').addEventListener('click', function() {
        vm.greenFlag();
    });
    document.getElementById('stopall').addEventListener('click', function() {
        vm.stopAll();
    });
    document.getElementById('turbomode').addEventListener('change', function() {
        var turboOn = document.getElementById('turbomode').checked;
        vm.setTurboMode(turboOn);
    });
    document.getElementById('compatmode').addEventListener('change',
    function() {
        var compatibilityMode = document.getElementById('compatmode').checked;
        vm.setCompatibilityMode(compatibilityMode);
    });
    var tabBlockExplorer = document.getElementById('tab-blockexplorer');
    var tabThreadExplorer = document.getElementById('tab-threadexplorer');
    var tabRenderExplorer = document.getElementById('tab-renderexplorer');
    var tabImportExport = document.getElementById('tab-importexport');

    // Handlers to show different explorers.
    document.getElementById('threadexplorer-link').addEventListener('click',
        function () {
            window.exploreTabOpen = true;
            getPlaygroundData();
            tabBlockExplorer.style.display = 'none';
            tabRenderExplorer.style.display = 'none';
            tabThreadExplorer.style.display = 'block';
            tabImportExport.style.display = 'none';
        });
    document.getElementById('blockexplorer-link').addEventListener('click',
        function () {
            window.exploreTabOpen = true;
            getPlaygroundData();
            tabBlockExplorer.style.display = 'block';
            tabRenderExplorer.style.display = 'none';
            tabThreadExplorer.style.display = 'none';
            tabImportExport.style.display = 'none';
        });
    document.getElementById('renderexplorer-link').addEventListener('click',
        function () {
            window.exploreTabOpen = false;
            tabBlockExplorer.style.display = 'none';
            tabRenderExplorer.style.display = 'block';
            tabThreadExplorer.style.display = 'none';
            tabImportExport.style.display = 'none';
        });
    document.getElementById('importexport-link').addEventListener('click',
        function () {
            window.exploreTabOpen = false;
            tabBlockExplorer.style.display = 'none';
            tabRenderExplorer.style.display = 'none';
            tabThreadExplorer.style.display = 'none';
            tabImportExport.style.display = 'block';
        });
};