var cons;   // PRE node containing characters
var upload; // File upload dialog
var charQueue = '';   // Characters waiting to be printed
var state = '';  // State last set by server
var lines = 0;  // Number of newlines printed
var cols = 0;   // Number of chars on this line
var buf = '';   // Input buffer
var on = true;  // Console turned on

var backend = '/cgi-bin/pig.cgi';
var max_lines = 17;
var max_cols = 56;

// Put a single character on the console
function putchar(ch) {
    cons.childNodes[0].nodeValue += ch;
    if (ch == "\n" || ++cols == max_cols) {
	 	  if (ch != "\n") {
        	  cons.childNodes[0].nodeValue += "\n";
		  }
        cols = 0;

        if (++lines == max_lines) {
            // Delete the last line
            var pos = cons.childNodes[0].nodeValue.indexOf("\n");
            
            cons.childNodes[0].nodeValue = 
                cons.childNodes[0].nodeValue.substr(pos+1);

            lines = max_lines - 1;
        }
    }
}

// Delete the last character up to a newline
function delchar() {
    var len = cons.childNodes[0].nodeValue.length;
    if (cons.childNodes[0].nodeValue[len-1] != "\n") {
        cons.childNodes[0].nodeValue =
            cons.childNodes[0].nodeValue.substr(0, len - 1)
    }
}

// Consume the next character from the queue
function consumeChar() {
    if (charQueue.length > 0) {
			  var c = charQueue.charAt(0);
			  charQueue = charQueue.substring(1);
			  putchar(c);
    }
    setTimeout("consumeChar()", 50);
}

// Print a string to the console
function print(str) {
    charQueue += str.toUpperCase();	
}

// Process Pig XML
function process(xml) {
    pigNode = xml.childNodes[0];
    for (var i = 1; i < pigNode.childNodes.length; i++) {
        cmd = pigNode.childNodes[i];

        switch (cmd.localName) {
        case 'print':
            print(cmd.textContent);
            break;
        case 'state':
            state = cmd.textContent;
            break;
        case 'upload':
            cons.style.visibility = 'hidden';
            upload.style.visibility = 'visible';
            break;
        case 'off':
            print("\n*** MECHANICAL PIG DISCONNECTED ***\n");
            on = false;
            break;
        }
    }
}

// Try to connect to the backend
function connect() {
    print('connecting to pig..... ');

    sendHttpReq(backend, 'cmd=init', function(xml) {
        print('OK\n');
        process(xml);
    });
}

// Communicate with the server
function sendHttpReq(url, vars, callbackFunc) {
    var request = window.XMLHttpRequest 
        ? new XMLHttpRequest() 
        : new ActiveXObject("MSXML2.XMLHTTP.3.0");
    
    request.open("POST", url, true);
    request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
    
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                if (request.responseText) {
                    var xmlobject;
                    if (window.ActiveXObject) {
            			      // No idea if this really works in IE
            			      xmlobject = new ActiveXObject("Microsoft.XMLDOM");
            			      xmlobject.async = "false";
            			      xmlobject.loadXML(request.responseText);
                    }
                    else {
            			      // Mozilla, etc.
            			      xmlobject = 
                            (new DOMParser()).parseFromString(
                                request.responseText, "text/xml");
                    } 
                    callbackFunc(xmlobject);
       			    }
            }
            else {
                print("\n*** ERROR ***\n");
                print("pig responded with status: " + request.status); 
            }
        }
    };
    request.send(vars);
}

// Called after every keypress
function keypress(ev)
{    
    if (!on || state == 'upload')
        return;

    if (!ev)
        ev = window.event;

    var dontprop = false;
    
    var kc;
    if (ev.keyCode) kc = ev.keyCode;
    if (ev.which)   kc = ev.which;
    
    if (kc == 13) {
        // Newline
        putchar("\n");
        var vs = 'cmd=submit&state=' + state
            + '&input=' + buf.toLowerCase();
        sendHttpReq(backend, vs, process);
        buf = '';
        
        dontprop = true;
    }
    else if (kc == 8) {
        // Backspace
        delchar();
    }
    else if (kc >= 32 && kc <= 126) { 
        var k = String.fromCharCode(kc);
        
        print(k);

        buf += k;
        
        dontprop = (k == ' ' || k == '/');
    }

    if (dontprop) {
        // Stop the event propagating
        ev.cancelBubble = true;
        if (ev.stopPropagation) ev.stopPropagation();
        if (ev.preventDefault)  ev.preventDefault();
        try { ev.keyCode = 0; } catch(e) {}
    }
}

// Called when the user uploads a file
function upload_file() {
    var file = document.getElementById('fileinput');
    var name = document.getElementById('nameinput');

    var fdata = file.files[0].getAsBinary();

    var cmd = 'cmd=upload' + '&name=' + name.value
        + '&file=' + fdata;
    sendHttpReq(backend, cmd, function(xml) {
        print('OK\n');
        process(xml);
    });

    print('Uploading to pig.... ');

    upload.style.visibility = 'hidden';
    cons.style.visibility = 'visible';
}

window.onload = function() {
    var div = document.getElementById('term');
    
    cons = document.createElement('PRE');
    div.appendChild(cons);
    
    var consBody = document.createTextNode('');
    cons.appendChild(consBody);
    
    print('*** MECHANICAL PIG CONSOLE v1.0 ***\n\n');
    consumeChar();
    
    print('initialising terminal..... ');

    upload = document.getElementById('upload');
    upload.style.visibility = 'hidden';

    document.onkeypress = keypress;

    print(' OK\n');
    
    connect();
}

