/* 
 This file was generated by Dashcode.  
 You may edit this file to customize your widget or web page 
 according to the license.txt file included in the project.
 */

//
// Class: CommandMonitor
// Base class for monitors that process the output of an external command.
// Takes care of the timers and output handlers so subclasses can just
// deal with the data as it comes in.
//

//
// CommandMonitor constructor
// Sets up some default values you can change in your subclass
//
// timeOut: If no output is received for timeOut seconds, watchdog is called
// restartAfter: Re-launch external command after this long if it exits
//
function CommandMonitor()
{
    this.timeOut = 300;
    this.restartAfter = 60;
}

//
// Method: start()
// Public interface to start your command monitor
//
CommandMonitor.prototype.start = function ()
{
    if (this.commandLine != null) {
        if (this.systemCommand != null) {
            alert("Warning: attempt to re-enter command " + this.commandLine);
            this.stop();
        }

        var self = this;
        var onFinished = function (command) { self.finishedHandler(command); };
        var onOutput = function (output) { self.outputHandler(output); };

        this.startWatchdogTimer();
        this.systemCommand = widget.system(this.commandLine, onFinished);
        this.systemCommand.onreadoutput = onOutput;
    }
}

//
// Method: stop()
// Public interface to stop your monitor
//
CommandMonitor.prototype.stop = function ()
{
    this.stopWatchdogTimer();

    if (this.startTimer != null) {
        clearTimeout(this.startTimer);
        delete this.startTimer;
    }

    if (this.systemCommand != null) {
        this.systemCommand.cancel();
        delete this.systemCommand;
    }
}

//
// Method: cancel()
// Public interface to cancel current command.
// It will be re-started automatically.
//
CommandMonitor.prototype.cancel = function ()
{
    this.stop();
    this.startAfterInterval();
}

//
// Method: processLine(line)
// You must override this method in your subclass.  It will be called
// once for each line your external command outputs.  This is where you
// do all your work.
//
// line: A single line of output from the external command
//
CommandMonitor.prototype.processLine = function (line)
{
    // alert("Virtual processLine: " + line);
}

//
// The rest of the methods in this class are not meant for public use
//

CommandMonitor.prototype.startAfterInterval = function ()
{
    if (this.restartAfter != null) {
        if (this.startTimer != null) {
            clearTimeout(this.startTimer);
        }

        var self = this;
        this.startTimer = setTimeout(function () {
            delete self.startInterval;
            self.start();
        }, this.restartAfter * 1000);
    }
}

CommandMonitor.prototype.startWatchdogTimer = function ()
{
    this.stopWatchdogTimer();

    var self = this;
    this.watchdogTimer = setTimeout(function () { self.watchdogTimeout(); }, this.timeOut * 1000);
}

CommandMonitor.prototype.stopWatchdogTimer = function ()
{
    if (this.watchdogTimer != null) {
        clearTimeout(this.watchdogTimer);
        delete this.watchdogTimer;
    }
}

CommandMonitor.prototype.watchdogTimeout = function ()
{
    alert("watchdog timeout");
    delete this.watchdogTimer;
    this.cancel();
}

CommandMonitor.prototype.outputHandler = function (output)
{
    this.stopWatchdogTimer();
    this.processLines(output);
    this.startWatchdogTimer();
}

CommandMonitor.prototype.processLines = function (lines)
{
    if (lines != null && lines.length > 0) {
        // Normalize line endings
        lines = lines.replace(/(\x0d\x0a)+/g, "\x0a");

        var match;
        var linePattern = /^.+$/mg;
        while ((match = linePattern.exec(lines)) != null) {
            var line = match[0];
            this.processLine(line);
        }
    }
}

CommandMonitor.prototype.finishedHandler = function (command)
{
    // alert("finished");
    this.stopWatchdogTimer();
    this.processLines(command.outputString);
    delete this.systemCommand;
    this.startAfterInterval();
}

//
// Class: IOStatMonitor
// CommandMonitor subclass that processes the output of "iostat".
// Uses two callbacks to return the load average and device I/O statistics
//

function IOStatMonitor (frequency, ioCallback, loadCallback) {
    this.commandLine = "/usr/sbin/iostat -d -C -w " + frequency;
    this.ioCallback = ioCallback;
    this.loadCallback = loadCallback;
}

IOStatMonitor.prototype = new CommandMonitor();

IOStatMonitor.prototype.processLine = function (line)
{
    //  KB/t tps  MB/s   KB/t tps  MB/s   KB/t tps  MB/s  us sy id
    // 20.06   5  0.09   9.70   0  0.00  10.67   0  0.00   4  3 92

    // Take off leading and trailing spaces
    line = line.replace(/^\s+/, "");
    line = line.replace(/\s+$/, "");

    // Look for a line of numbers
    var match;
    if ((match = line.match(/^(\s*[\d\.]+)+$/)) != null) {
        var n = match[0].split(/\s+/);

        if (n.length > 0 && n.length % 3 == 0) {
            // Add up all the MB/s numbers
            var mbps = 0.0;
            for (var i = 2; i < n.length - 3; i += 3) {
                mbps += parseFloat(n[i]);
            }

            // Grab the inverse of the CPU idle time and call it load
            var load = 100 - parseInt(n[n.length - 1]);

            // alert("disk io: " + mbps + ", load: " + load);
            if (this.ioCallback != null) {
                this.ioCallback(mbps);
            }
            if (this.loadCallback != null) {
                this.loadCallback(load);
            }
        }
        else {
            alert("iostat format violation: expecting groups of three:");
            alert(line);
        }
    }
}

//
// Class: NetStatMonitor
// CommandMonitor subclass that processes the output of "netstat".
// Returns network input and output stats via a callback.
//

function NetStatMonitor (frequency, ioCallback) {
    this.commandLine = "/usr/sbin/netstat -w " + frequency;
    this.ioCallback = ioCallback;
}

NetStatMonitor.prototype = new CommandMonitor();

NetStatMonitor.prototype.processLine = function (line)
{
    //          input        (Total)           output
    // packets  errs      bytes    packets  errs      bytes colls
    //       0     0          0          0     0          0     0

    // Take off leading and trailing spaces
    line = line.replace(/^\s+/, "");
    line = line.replace(/\s+$/, "");

    // Look for a line of numbers
    var match;
    if ((match = line.match(/^(\s*[\d\.]+)+$/)) != null) {
        var n = match[0].split(/\s+/);

        if (n.length == 7) {
            var inBytes = parseInt(n[2]);
            var outBytes = parseInt(n[5]);

            // alert("network io: " + inBytes + "/" + outBytes);
            if (this.ioCallback != null) {
                this.ioCallback(inBytes, outBytes);
            }
        }
        else {
            alert("netstat format violation: expecting seven values:");
            alert(line);
        }
    }
}
