/* Weather Station Display
 * 
 * This file when used in combination with the files wshead.php, wsbody.php,
 * and ws.css allows the adition of a dynamic weather display to a web page.
 * It makes use of an AJAX connection to the host system to retrive weather
 * data and then updates the display to reflect this data. Animation of the
 * various dials and gauges is provided.
 *
 * Copyright 2005, 2006 Andy Shaw
 */

// Create the AJAX connection
var cp = new cpaint();
cp.set_persistent_connection(true);
cp.set_async(true);
cp.set_proxy_url('');
cp.set_debug(-1);
cp.set_response_type('JSON');



function Pointer(res, loc)
{
    // Basic pointer handling class, Load and animate a set of pointer
    // bitmaps

    // We load 360/res bitmaps each with a pointer image at the appropriate
    // angle. These are used for the annimation.
    var ptr = new Array(360/res);

    function doAnimate(item, from, to, dir)
    {
        // Animate the pointer move. Use Javascript colosures to perform
        // callback to this function preserving state.

        // Select the correct pointer bitmap and display it.
        document.getElementById(item).src = ptr[from].src;
        if (from != to)
        {
            // Handle circular move
            from += dir;
            if (from < 0)
                from = ptr.length-1;
            else if (from >= ptr.length)
                from = 0;
            window.setTimeout(function(){doAnimate(item, from, to, dir)}, 150);
        }
    }

    this.animate = function(item, from, to, dir)
    {
        // public method to actually move the pointer, input is 0-360 degrees
        // Yes it really is 0-360 0 is a special case indicating calm wind! So
        // We allow both 0 and 360.
        to = Math.round(to/res);
        // make sure to is in range
        if (to >= ptr.length) to = 0;
        if (to < 0) to = 0;
        from = Math.round(from/res);
        // make sure to is in range
        if (from >= ptr.length) from = 0;
        if (from < 0) from = 0;
        doAnimate(item, from, to, dir);
    }


    this.load = function(progress)
    {
        // Load the bitmaps
        var i;
        for(i=0; i < ptr.length; i++)
        {
            ptr[i] = new Image(165, 165);
            if (progress)
                ptr[i].onload = progress;
            ptr[i].src = loc+i*res;
        }
    }

}

function Trend()
{
    var img = new Array(3);

    this.load = function(progress)
    {
        for(var i=0; i < img.length; i++)
        {
            img[i] = new Image(12, 12);
            if (progress) img[i].onload = progress;
            img[i].src = WS_BASE + "dials.php/trend.png?type=trend"+i;
        }
    }

    this.set = function(item, lab, val)
    {
        var loc;
        if (val == 0)
            loc = 1;
        else if (val > 0)
            loc = 2;
        else
        {
            loc = 0;
            val = -val;
        }
        document.getElementById(item).src = img[loc].src;
        document.getElementById(lab).innerHTML = val;
    }
}

function gaugeValue()
{
    function doAnimate(item, from, to, dir)
    {
        document.getElementById(item).style.height = from+"px";
        if (from != to)
        {
            from += dir;
            window.setTimeout(function(){doAnimate(item, from, to, dir)}, 20);
        }
    }

    this.animate = function(item, from, to)
    {
        // convert to int pixels from %
        from = Math.round((from*150)/100);
        to = Math.round((to*150)/100);
        if (to > from)
            doAnimate(item, from, to, 1);
        else
            doAnimate(item, from, to, -1);
    }
}
        

function gaugeMarker()
{
    function doAnimate(item, from, to, dir)
    {
        document.getElementById(item).style.bottom = from+"px";
        if (from != to)
        {
            from += dir;
            window.setTimeout(function(){doAnimate(item, from, to, dir)}, 20);
        }
    }

    this.animate = function(item, from, to)
    {
        // convert to int pixels from %
        from = Math.round((from*150)/100);
        to = Math.round((to*150)/100);
        if (to > from)
            doAnimate(item, from, to, 1);
        else
            doAnimate(item, from, to, -1);
    }
}
        

function DirectionPtr(item, ptr, initial)
{
    // Class to bind a pointer to a dial in the html and actually
    // move the pointer. This version assumes a compass type display
    // with 0 at the top and moves the pointer in the shortest direction.
    var from = initial;

    this.move = function(to)
    {
        // Do the move. Work out shortest direction
        if (from > to)
        {
            if (from - to > 180)
                ptr.animate(item, from, to, 1);
            else
                ptr.animate(item, from, to, -1);
        }
        else
        {
            if (to - from > 180)
                ptr.animate(item, from, to, -1);
            else
                ptr.animate(item, from, to, 1);
        }
        // remember our new location
        from = to;
    }


    // Move pointer to initial location
    this.move(initial);
}
                        
function SpeedPtr(item, ptr, initial, max)
{
    // Class to bind a pointer to a dial in the html and actually
    // move the pointer. This version assumes a speed type display
    // with 0 at the lower left and moves the pointer in a linear direction.
    var from = initial;

    function convert(p)
    {
        // Peg the end stop at max
        if (p > max) p = max;
        // Scale from kph to degrees
        p = (p*303.75)/max;
        // move zero point from north to lower left
        p += 208.125;
        if (p >= 360) p -= 360;
        return p;
    }

    this.move = function(to)
    {
        // Do the move.
        if (from > to)
            ptr.animate(item, convert(from), convert(to), -1);
        else
            ptr.animate(item, convert(from), convert(to), 1);
        // remember our new location
        from = to;
    }


    // Move pointer to initial location
    this.move(initial);
}


function linearGauge(item, ptr, scale, offset, initial)
{
    // Class to control linear gauge in the html. Provides annimation of
    // changes in the reading of the gauge. Allows scaling and offset values.
    var from = convert(initial);

    function convert(t)
    {
        return ((t-offset)*100)/scale;
    }

    this.move=function(to)
    {
        to = convert(to);
        if (to < 0) to = 0;
        if (to > 100) to = 100;
        ptr.animate(item, from, to);
        from = to;
    }

    this.move(initial);
}
       
    
                        
var dirPtr = new Pointer(5.625, WS_BASE + "dials.php/dirptr.png?type=dirptr&angle=");
var speedPtr = new Pointer(5.625, WS_BASE + "dials.php/speedptr.png?type=speedptr&angle=");
var marker = new Pointer(5.625, WS_BASE + "dials.php/mark.png?type=mark&angle=");
var trend = new Trend();
var windDir;
var windSpeed;
var windGust;
var windAvgDir;
var windMaxSpeed;
var windMaxGust;
var temp;
var tempMin;
var tempMax;
var wc;
var wcMin;
var dewpoint;
var rain1h;
var rain24h;
var rainTotal;
var hum;
var pressure;
var busy = false;
var timeout = CONNTIMEOUT;
var dataItems = new Array("D_WINDDIR", "D_WINDSPEED", "D_CURWINDDIR", "D_CURWINDSPEED", "D_WINDSPEED1H", "D_WINDGUST1H", "D_DATE", "D_TIME", "D_TEMPOUTDOORS", "D_TEMPMIN", "D_TEMPMAX", "D_WINDCHILL", "D_DEWPOINT", "D_HUMIDITYOUTDOORS", "D_RAIN1H", "D_RAIN24H", "D_RAINTOTAL", "D_PRESSURE", "D_WINDCHILLMIN", "D_HUMIDITYMIN", "D_HUMIDITYMAX", "D_RAINRATE", "D_TEMPRATE", "D_PRESSURERATE", "D_PRESSURERATE24"); 

	
function getWeatherData() 
{
    // Request the data from the server using Ajax.
    // Turn on the data active light
    if (busy)
        document.getElementById('dataActive').style.backgroundColor = "red";
    else
        document.getElementById('dataActive').style.backgroundColor = "green";

    busy = true;
    cp.call(WS_BASE + 'weatherdata.php', 'getWeatherData', response, "1");
    // Get it again in 8 seconds
    if (timeout > 0)
        timeout -= 8;
    if (timeout >= 0)
        window.setTimeout('getWeatherData()', 8000);
    else
        document.getElementById('suspended').style.display = "block";
    return false;
}

function response(result) 
{
    // Got a response from the server. Update everything
    if (result != null)
    {
        var info = result.data;
        var cloudBase;
	
        // Update the data elements
        for (var i=0; dataItems[i] != "D_RAINRATE"; i++)
            document.getElementById(dataItems[i]).innerHTML = info[dataItems[i]];
        trend.set("raintrend", "D_RAINRATE", info.D_RAINRATE);
        trend.set("pressure1htrend", "D_PRESSURERATE", info.D_PRESSURERATE);
        trend.set("pressure24htrend", "D_PRESSURERATE24", info.D_PRESSURERATE24);
        trend.set("temptrend", "D_TEMPRATE", info.D_TEMPRATE);
        // Update the dials
        windDir.move(info.D_CURWINDDIR);
        windAvgDir.move(info.D_WINDDIR);
        windSpeed.move(info.D_WINDSPEED);
        windMaxSpeed.move(info.D_WINDSPEED1H);
        windGust.move(info.D_CURWINDSPEED);
        windMaxGust.move(info.D_WINDGUST1H);
        temp.move(info.D_TEMPOUTDOORS);
        tempMin.move(info.D_TEMPMIN);
        tempMax.move(info.D_TEMPMAX);
        wc.move(info.D_WINDCHILL);
        wcMin.move(info.D_WINDCHILLMIN);
        dewpoint.move(info.D_DEWPOINT);
        hum.move(info.D_HUMIDITYOUTDOORS);
        rain1h.move(info.D_RAIN1H);
        rain24h.move(info.D_RAIN24H);
        rainTotal.move(info.D_RAINTOTAL);
        pressure.move(info.D_PRESSURE);

        // Work out what the cloud base is
        cloudBase = (info.D_TEMPOUTDOORS - info.D_DEWPOINT)/0.00802;
        cloudBase += WS_ELEVATION;
        document.getElementById("D_CLOUDBASE").innerHTML = Math.round(cloudBase);
   
        // Turn off data active light
        document.getElementById('dataActive').style.backgroundColor = "transparent";
        busy = false;
    }
}


function onComplete()
{
    // Called when ready to go, all images etc. preloaded

    // Create and bind animation classes
    windDir = new DirectionPtr("windDirPtr", dirPtr, 180);
    windAvgDir = new DirectionPtr("windAvgDirPtr", marker, 180);
    windSpeed = new SpeedPtr("windSpeedPtr", speedPtr, 0, 80);
    windMaxSpeed = new SpeedPtr("windMaxSpeedPtr", marker, 0, 80);
    windGust = new SpeedPtr("windGustPtr", speedPtr, 0, 80);
    windMaxGust = new SpeedPtr("windMaxGustPtr", marker, 0, 80);
    temp = new linearGauge("temp", new gaugeValue(), 50, -10, -10);
    tempMin = new linearGauge("tempMin", new gaugeMarker(), 50, -10, -10);
    tempMax = new linearGauge("tempMax", new gaugeMarker(), 50, -10, -10);
    wc = new linearGauge("windchill", new gaugeValue(), 50, -10, -10);
    wcMin = new linearGauge("wcMin", new gaugeMarker(), 50, -10, -10);
    dewpoint = new linearGauge("dewpoint", new gaugeValue(), 50, -10, -10);
    rain1h = new linearGauge("rain1h", new gaugeValue(), 5, 0, 0);
    rain24h = new linearGauge("rain24h", new gaugeValue(), 50, 0, 0);
    rainTotal = new linearGauge("rainTotal", new gaugeValue(), 500, 0, 0);
    hum = new linearGauge("humidity", new gaugeValue(), 100, 0, 0);
    pressure = new linearGauge("pressure", new gaugeValue(), 100, 950, 950);
    // Hide the progress screen
    document.getElementById('progress').style.display = "none";
    // Start requesting data.
    getWeatherData();
}


function onContinue()
{
    // Hide the progress screen
    document.getElementById('suspended').style.display = "none";
    timeout = CONNTIMEOUT;
    // Start requesting data.
    getWeatherData();
}


var totalLoad;
var loaded;

function progress()
{
    // Keep track of how many items we have loaded. When we have them all
    // start the staation running.
    loaded++;;
    document.getElementById('progressBarText').innerHTML = Math.round((loaded*100)/totalLoad)+"%";
    document.getElementById('progressBar').style.width = Math.round((loaded*100)/totalLoad)+"%";
    if (loaded == 1*64) speedPtr.load(progress);
    if (loaded == 2*64) marker.load(progress);
    if (loaded == 3*64) trend.load(progress);
    if (loaded >= totalLoad) window.setTimeout("onComplete()", 1500);
}

function onLoad()
{
    // all static page elements ready to go.

    // Can we run in this environment?
    if (cp.capable)
    {
        // Yes so start the preload process
        loaded = 0;
        totalLoad = 64*3+3;
        dirPtr.load(progress);
        //speedPtr.load(progress);
        //marker.load(progress);
        //trend.load(progress);
    }
    else
        document.getElementById('error').style.display = "block";
}

// Tooltip generation functions...

function ts()
{
    // return a timestamp to make urls for images unique to prevent caching
    var now = new Date();
    return "&ts=" + now.getYear() + now.getMonth() + now.getDay() + now.getHours() + now.getMinutes();
}


function graph(name, id, period)
{
    // generate a request to get a graph with period update
    ret = WS_BASE + name + "?period=" + period + "&size=tt";
    return "<img id='" + id + "' src='" + ret + ts() + "' width=400 height=250>";
}



function graph24h(name, id)
{
    return graph(name, id, "24h");
}

function graphWeek(name, id)
{
    return graph(name, id, "week");
}


function graphMonth(name, id)
{
    return graph(name, id, "month");
}
