var gorobots_init = function gorobots(my, webgl){
    my.width = null;
    my.height = null;
    my.server = "ws://localhost:8666/ws/";
    my.websocket = null;
    my.id = null;
    my.connection_retry = 3000;
    my.state = null;
    my.spectator = false;

    my.debug = false;
    my.debug_draw = true;

    var grey = "#999999";

    var colors = [
        "#222222",       // Grey
        "#000099",
        "#009900",
        "#990000",
        "#009999",
        "#990099",
        "#999900",
        "#000044",
        "#004400",
        "#440000",
        "#004444",
        "#440044",
        "#444400",
        "#0000cc",
        "#00cc00",
        "#cc0000",
        "#00cccc",
        "#cc00cc",
        "#cccc00"
    ];



    my.connect = function(server){
        connection = new WebSocket(server, null);
        my.websocket = connection;
        console.log(connection);
        connection.onerror = function (error) {
          console.log('WebSocket Error ' + error);
        };

        connection.onopen = function(){
            console.log("Connected to " + server);
            my.send_game_request();
        };

        connection.onclose = function(){
            my.id = null;
            my.state = null;
            my.renderer.reset();
            console.log("Connection Closed");

            if (my.connection_retry > 0){
                // Retry every few seconds
                console.log("Lost Connection: " + server);
                setTimeout(function(){
                    my.websocket = my.connect(my.server);
                }, my.connection_retry);
            }
        };

        connection.onmessage = function (e) {

            // Handshake protocol, we must establish a game ID and send
            // the robot config for approval before we can play

            new_data = JSON.parse(e.data);

            if (my.state === null) {
                if (new_data.type == "idreq") {
                    if ('id' in new_data){
                        my.id = new_data['id'];
                        console.log("Assigned ID " + my.id + " by server");
                    } else {
                        console.log("server failed to send us an id");
                    }
                    my.send_client_id();
                    my.state = "gameparam";
                    // send supported encodings
                    my.websocket.send(JSON.stringify(["json"]));
                }
            } else if (my.state == "gameparam"){
                if (new_data.type == "gameparam") {
                    // > [OK | FULL | NOT AUTH], board size, game params
                    my.parse_game_params(new_data);
                    my.state = "handshake";
                    my.setup_robot();
                }
            } else if (my.state == "handshake") {
                // We have game details, make the render target
                console.log("completed handshake");
                console.log(new_data);
                if('success' in new_data) {
                    if (!new_data.success){
                        alert("Your Robot Config is not valid, adjust your point distributions and try again");
                        return false;
                    }
                }
                my.renderer.setup_render_target(my.width, my.height, my.width, my.height);
                my.state = "play";
            } else if(my.state == "play") {
                if (new_data.type == "handshake") {
                    // This is the handshake response, we've been assigned an ID
                    // and are in the game (or TODO: in a lobby).
                    console.log("we got a handshake when we expect a boardstate packet");
                } else if (new_data.type == "stats") {
                    // XXX: need to actually assign this to the player/robots
                    // ...
                    console.log("\\o/ we have stats: " + JSON.stringify(new_data));
                } else if (new_data.type == "boardstate") {
                    my.renderer.begin_frame(new_data['turn']);
                    var draw_list = my.process_gameplay_packet(new_data);
                    my.renderer.render(draw_list);
                    my.renderer.end_frame();
                } else if (new_data.type == "gameover") {
                    my.renderer.reset();
                } else {
                    console.error("unexpected state ...");
                }
            }
        };

        return connection;
    };

    my.prepare_data = function(data){

        // get all the robot id's in the scanner list and find them in the
        // overall robot list so we can pass them to the robot code
        if (data['my_robots']){
            for (var i=0; i < data['my_robots'].length; i++){
                var bot = data['my_robots'][i];
                bot.messages = data.messages;

                for(var p in bot['scanners']){
                    var bot_id = bot['scanners'][p]['id'];

                    // find the scanner object in the pool of objects
                    if (bot['scanners'][p]['type'] == 'robot'){

                        for (var b in data['robots']){
                            if (data['robots'][b]['id'] == bot_id){
                                bot['scanners'][p] = data['robots'][b];
                                bot['scanners'][p].type = "robot";
                                bot['scanners'][p].friend = false;
                            }
                        }

                        for (var mybot in data['my_robots']){
                            if (data['my_robots'][mybot].id == bot['scanners'][p].id){
                                bot['scanners'][p].friend = true;
                            }
                        }
                    }
                    if (bot['scanners'][p]['type'] == 'projectile'){
                        for (var b in data['projectiles']){
                            if (data['projectiles'][b]['id'] == bot_id){
                                bot['scanners'][p] = data['projectiles'][b];
                                bot['scanners'][p].type = "projectile";
                            }
                        }
                    }
                }
            }
        }

        return data;
    };

    my.process_gameplay_packet = function(new_data){

        my.stats.begin();
        my.renderer.clear_debug();

        var draw_list = [];

        if (new_data.reset){
//            my.setup_robot();
            my.renderer.reset();
            return;
        }

        if (!my.paused) {
            var debug_div = document.getElementById("raw-debug");
            if (debug_div){
                debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
            }
        }

        // Prep the data
        new_data = my.prepare_data(new_data);

        // Dump the contents of the message from the server
        if (!my.paused) {
            var debug_div = document.getElementById("debug");
            if (debug_div){
                debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
           }
        }

        // Update the robots and build a list of what to draw
        var my_robots = new_data['my_robots'];
        var robots = new_data['robots'];
        var all_bots = new_data['all_bots'];

        my.all_bots = [];
        my.my_bots = my_robots;
        // Set the list of players and assign colors
        var players = "";

        var col = 2;
        for (i=0; i < all_bots.length; i++){
            my.all_bots.push(all_bots[i]['robot_id']);

            // I hate javascript
            var health_str = "";
            if (all_bots[i]['health'] < 0) {
                health_str = "000";
                all_bots[i]['color'] = grey;
            }
            else if (all_bots[i]['health'] < 10) {
                health_str = "00" + all_bots[i]['health'];
                all_bots[i]['color'] = colors[col];
            }
            else if (all_bots[i]['health'] < 100){
                health_str = "0" + all_bots[i]['health'];
                all_bots[i]['color'] = colors[col];
            }
            else{
                health_str = all_bots[i]['health'];
                all_bots[i]['color'] = colors[col];
            }

            var mine = false;
            for (var mybot in my.my_bots){
                if (my.my_bots[mybot].id == all_bots[i]['robot_id']){
                    mine = true;
                    all_bots[i]['color'] = colors[1];
                }
            }
            if (!mine){
                col++;
            }

            players += ("<span style='color: " +
                all_bots[i]['color'] +
                ";'>&nbsp&nbsp" +
                all_bots[i]['robot_id'] +
                " [" + health_str + "]</span>");
        }

        var players_div = document.getElementById("players");
        if (players_div){
            players_div.innerHTML = players;
        }

        // Now let's deal with all the things in the world
        var i = 0;
        if (my_robots){
            instructions = {};
            for (i=0; i < my_robots.length; i++){
                if (my_robots[i].health > 0) {
                    instructions[my_robots[i]['id']] = my.update_robot(my_robots[i], new_data['obj']);
                }

                for (var bot in all_bots){
                    if (all_bots[bot]['robot_id'] == my_robots[i]['id']){
                        col = all_bots[bot]['color'];
                    }
                }

                if (my_robots[i].health <= 0){
                    col = grey;
                }

                if ("position" in my_robots[i]){
                    my_robots[i]['color'] = col;
                    my_robots[i]['type'] = 'robot';
                    draw_list.push(my_robots[i]);
                }

            }

            if (my.websocket){
                var payload = JSON.stringify(instructions);
                // console.log(payload);
                my.websocket.send(payload);
            }

            if (my.target < 0 && !my.spectator){
                console.log("Watching " + my.my_bots[0].id);
                my.renderer.set_focus_robot(my.my_bots[0].id);
                my.target = 0;
            }
        }

        if (robots){
            for (i=0; i < robots.length; i++){

                // Get the color from all_bots
                var col = grey;
                for (var bot in all_bots){
                    if (all_bots[bot]['robot_id'] == robots[i]['id']){
                        col = all_bots[bot]['color'];
                    }
                }

                if (robots[i].health <= 0){
                    col = grey;
                }

                if ("position" in robots[i]){
                    robots[i]['color'] = col;
                    robots[i]['type'] = 'robot';
                    draw_list.push(robots[i]);
                }
            }
        }

        // Draw the projecticles
        var projectiles = new_data['projectiles'];
        if (projectiles){
            for (i=0; i < projectiles.length; i++){

                if ("position" in projectiles[i]){
                    projectiles[i]['type'] = 'bullet';
                    draw_list.push(projectiles[i]);
                }
            }
        }

        // Draw the projecticles
        var splosions = new_data['splosions'];
        if (splosions){
            for (i=0; i < splosions.length; i++){
                if ("position" in splosions[i]){
                    splosions[i]['type'] = 'explosion';
                    draw_list.push(splosions[i]);
                }
            }
        }

        // Draw the objects
        var obj = new_data['objects'];
        if (obj){
            for (i=0; i < obj.length; i++){
                var o = {
                    bounds : obj[i]
                };
                o['type'] = "object";
                o['position'] = {x: obj[i][0], y: obj[i][1]};
                draw_list.push(o);
            }
        }
        my.stats.end();

        return draw_list;
    };

    my.eval_input = function( input, output ){
        var theResult, evalSucceeded;

        try{
            theResult = eval( input );
            evalSucceeded = true;
        }
        catch(e){
            output.innerHTML = e;
        }

        if ( evalSucceeded ) {
            output.innerHTML = "";
            // console.log("OK");
        }

        return theResult;
    };

    my.get_robot_code = function(){
        var robot_code = editor.getSession().getValue();
        var output = document.getElementById('output');

        var code = "( " + robot_code + " )";
        var rc = my.eval_input(code, output);

        return rc.call(this);
    };

    my.send_game_request = function() {
        game = {
            "id": my.game_id,
        };
        console.log(my.websocket.readyState);
        my.websocket.send(JSON.stringify(game));
        console.log("sent clientid: " + JSON.stringify(game));
    };

    my.send_client_id = function() {
        client_id = {
            "type": my.spectator ? "spectator" : "robot",
            "name": "dummy",
            "id": "24601",
            "useragent": "gorobots.js",
        };
        my.websocket.send(JSON.stringify(client_id));
    };

    my.parse_game_params = function(params) {
        // TODO: flesh out validation?
        my.width = new_data.boardsize.width;
        my.height = new_data.boardsize.height;
        return true;
    };

    my.setup_robot = function(){
        var robot = my.get_robot_code();
        if ('setup' in robot){
            var map = {"width": my.width, "height": my.height};
            var config = {
                "stats": robot.setup(map),
                "id": my.id
            };
            // console.log(config);
            if (!my.spectator) {
                my.websocket.send(JSON.stringify(config));
            }
        }
    };

    my.update_robot = function(data, objects){
        var robot = my.get_robot_code();
        var map = {"width": my.width, "height": my.height, "obj": objects};

        var instructions = null;
        if ('update' in robot){

            var dbg = {
                "msg" : function(x, y, msg){
                    my.renderer.debug_label(x,y,msg);
                },
                "msg_screen" : function(x, y, msg){
                    my.renderer.debug_label_screen(x,y,msg);
                },
                "line" : function(x, y, x2, y2){
                    my.renderer.debug_line(x,y,x2,y2);
                },
            };

            instructions = robot.update(data, map, dbg);
        }
        return instructions;
    };

    my.set_spectator = function(s){
        my.spectator = s;
        my.target=-1;
        $('body').keypress(function(e){
            if (my.renderer && my.renderer.canvas.clientWidth === 0){
                // dont respond to key events when canvas is not in view
                return;
            }

            if(e.which == 116){
                // t
                if (my.spectator){
                    my.target++;
                    if( my.target < my.all_bots.length){
                       console.log("Watching " + my.all_bots[my.target]);
                        my.renderer.set_focus_robot(my.all_bots[my.target]);
                    }
                    else {
                        my.target = -1;
                        my.renderer.set_focus_robot(null);
                    }
                } else {
                    my.target++;
                    if( my.target < my.my_bots.length){
                        console.log(my.my_bots);
                        console.log(my.my_bots[my.target].id);
                        console.log("Watching " + my.my_bots[my.target].id);
                        my.renderer.set_focus_robot(my.my_bots[my.target].id);
                    }
                    else {
                        my.target = -1;
                        my.renderer.set_focus_robot(null);
                    }
                }
            }
        });
    };

    my.set_server = function(server_name, game_id){
        my.server = server_name;
        my.game_id = game_id;
        my.state = null;
        if (my.websocket){
            console.log("Switching Server: " + my.server);
            my.websocket.close();
            my.renderer.shutdown();
        }
        else{
            console.log("Setting Server: " + my.server);
            my.websocket = my.connect(my.server);
        }
    };

    my.init = function(){
        console.log("Welcome to GoRobots");
        if (webgl){
            my.renderer = gorobots_render_webgl();
        }
        else{
            my.renderer = gorobots_render_canvas();
        }

        my.renderer.init("battlefield");

        my.stats = new Stats();
        my.stats.setMode(0); // 0: fps, 1: ms

        // Align top-left
        my.stats.domElement.style.position = 'absolute';

        my.stats.domElement.style.left = '5px';
        my.stats.domElement.style.top = '120px';

        document.body.appendChild( my.stats.domElement );
        $(my.stats.domElement).hide();

        my.paused = false;
        $('body').keypress(function(e){

            // alert(e.which);
            if(e.which == 112){
                // p
                my.paused = !my.paused;
            }

            if (my.renderer && my.renderer.canvas.clientWidth === 0){
                // dont respond to key events when canvas is not in view
                return;
            }

            if(e.which == 100){
                // d
                $(my.stats.domElement).toggle();
            }
        });


    };

    my.init();
    return my;
};
