// 
// This implements the NightshadeOverlay overlay object
// Copyright 2009 Philip Gladstone - N1DQ
// This is a subclass of a GTileLayerOverlay but has no constructor parameters.
// 

function NightshadeLayer(oo) {
    this.timeInterval = oo.timeInterval;
    this.overlayobject = oo;
    this.setCurrentTime();
    this.mousemoved = 0;
    this.wantShowLights = true;
    this.actuallyShowingLights = true;


    this.serverlist = [ 'http://night-shade.appspot.com', 'http://pskreporter.appspot.com' ];
    this.cachesuffix = '.nyud.net';
    //this.serverlist = [ 'http://localhost:8080' ];
    this.servermulfactor = 1;
}

(function () {
    var myCopyright = new GCopyrightCollection("© ");
    myCopyright.addCopyright(new GCopyright('Demo',
      new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)),
        0,'©2009 Philip Gladstone'));

    NightshadeLayer.prototype = new GTileLayer(myCopyright);

    var fmod = function (d, v) {
        var q = Math.floor(d / v);
        return d - q * v;
    }

    NightshadeLayer.prototype.getAltitude = function (ll, dt) {
        var fLatitude = ll.latRadians();
        var fLongitude = ll.lngRadians();

        // Calculate difference (in minutes) from reference longitude.
        var fDifference = (((fLongitude) * 180/Math.PI) * 4) / 60.0;

        // Caculate solar time.
        var fSolarTime = this.fLocalTime + this.fEquation + fDifference;

        // Calculate hour angle.
        var fHourAngle = (15 * (fSolarTime - 12)) * (Math.PI/180.0);

        // Calculate current altitude.
        var cc = Math.cos(this.fDeclination) * Math.cos(fLatitude);
        t = (Math.sin(this.fDeclination) * Math.sin(fLatitude)) + (cc * Math.cos(fHourAngle));
        var fAltitude = Math.asin(t);

        var result = new Object();
        result.altitude = fAltitude;
        result.slopeEast = -cc * Math.sin(fHourAngle);

        return result;
    }

    var sign = function (x) {
        if (x < 0) {
            return -1;
        }

        return 1;
    }

    NightshadeLayer.prototype.getUrlPrefix = function(pt, showingLights) 
    {
        var i = pt.x + pt.y * this.servermulfactor;
        var suffix = '';
        if (showingLights) {
            suffix = this.cachesuffix;
        }
        return this.serverlist[i % this.serverlist.length] + suffix + "/nightshade/";
    };

    NightshadeLayer.prototype.getTileUrl = function(pt, zoom) 
    { 
        var showLights = this.wantShowLights && (this.mousemoved < 2);
        this.actuallyShowingLights = showLights;

        if (zoom > 6) {
            showLights = false;
        }
        var maptype = this.overlayobject.nso_map.getCurrentMapType();
        var proj = maptype.getProjection();
        var tl = proj.fromPixelToLatLng(new GPoint(pt.x * 256, pt.y * 256), zoom);
        var tr = proj.fromPixelToLatLng(new GPoint(pt.x * 256 + 255, pt.y * 256), zoom);
        var bl = proj.fromPixelToLatLng(new GPoint(pt.x * 256, pt.y * 256 + 255), zoom);
        var br = proj.fromPixelToLatLng(new GPoint(pt.x * 256 + 255, pt.y * 256 + 255), zoom);

        var tla = this.getAltitude(tl);
        var tra = this.getAltitude(tr);
        var bla = this.getAltitude(bl);
        var bra = this.getAltitude(br);

        if (tla.altitude > this.overlayobject.max_alt && tra.altitude > this.overlayobject.max_alt &&
            bla.altitude > this.overlayobject.max_alt && bra.altitude > this.overlayobject.max_alt) {
            if (tla.slopeEast >= 0 || (tla.slopeEast < 0 && tra.slopeEast < 0)) {
                if (bla.slopeEast >= 0 || (bla.slopeEast < 0 && bra.slopeEast < 0)) {
                    return "http://night-shade.appspot.com/nightshade_0.png";
                }
            }
        }
        if (tla.altitude < this.overlayobject.min_alt && tra.altitude < this.overlayobject.min_alt &&
            bla.altitude < this.overlayobject.min_alt && bra.altitude < this.overlayobject.min_alt) {
            if (tla.slopeEast < 0 || (tla.slopeEast >= 0 && tra.slopeEast >= 0)) {
                if (bla.slopeEast < 0 || (bla.slopeEast >= 0 && bra.slopeEast >= 0)) {
                    if (showLights) {
                        return this.getUrlPrefix(pt, false) + pt.x + "-" + pt.y + "-" + zoom + "-" + "static" + "-" + this.opacity + "n.png"; 
                    }
                    return "http://night-shade.appspot.com/nightshade_" + this.opacity + ".png";
                }
            }
        }

        return this.getUrlPrefix(pt, showLights) + pt.x + "-" + pt.y + "-" + zoom + "-" + Math.round(this.currentTime/1000) + "-" + this.opacity + (showLights ? "n.png" : ".png"); 
    };

    NightshadeLayer.prototype.isPng = function() { return true;};

    NightshadeLayer.prototype.getOpacity = function() { 
        return 1; 
        //var maptype = this.overlayobject.nso_map.getCurrentMapType();
        //return (maptype == G_NORMAL_MAP) ? 0.5 : 0.75; 
        }

    NightshadeLayer.prototype.setCurrentTime = function () {
        if (this.overlayobject.nso_map) {
            var maptype = this.overlayobject.nso_map.getCurrentMapType();
            this.opacity = (maptype == G_NORMAL_MAP) ? 50 : 75; 
            this.opacity = 50; 
        }

        this.currentTime = new Date(this.timeInterval * 1000 * Math.round(new Date() / (this.timeInterval * 1000)));

        // Get julian date.
        var fJulianDate = this.currentTime / (1000 * 86400.0);

        // Get local time value.
        this.fLocalTime = (this.currentTime % 86400000) / (1000 * 3600.0);


        ////////////////////////////////////////////////////////////
        // CALCULATE SOLAR VALUES
        ////////////////////////////////////////////////////////////

        // Calculate solar declination as per Carruthers et al.
        var t = 2 * Math.PI * fmod((fJulianDate - 1) / 365.25, 1);

        var fDeclination = (0.322003
              - 22.971 * Math.cos(t)
              - 0.357898 * Math.cos(2*t)
              - 0.14398 * Math.cos(3*t)
              + 3.94638 * Math.sin(t)
              + 0.019334 * Math.sin(2*t)
              + 0.05928 * Math.sin(3*t)
              );

        // Convert degrees to radians.
        if (fDeclination > 89.9) fDeclination = 89.9;
        if (fDeclination < -89.9) fDeclination = -89.9;

        // Convert to radians.
        this.fDeclination = fDeclination * (Math.PI/180.0);

        // Calculate the equation of time as per Carruthers et al.
        t = fmod(279.134 + 0.985647 * fJulianDate, 360) * (Math.PI/180.0);

        var fEquation = (5.0323
              - 100.976 * Math.sin(t)
              + 595.275 * Math.sin(2*t)
              + 3.6858 * Math.sin(3*t)
              - 12.47 * Math.sin(4*t)
              - 430.847 * Math.cos(t)
              + 12.5024 * Math.cos(2*t)
              + 18.25 * Math.cos(3*t)
              );

        // Convert seconds to hours.
        this.fEquation = fEquation / 3600.00;
    };

    NightshadeLayer.prototype.constructor = NightshadeLayer;
})();

function NightshadeOverlay() {
    this.max_alt = 1.05 * Math.PI / 180.0;
    this.min_alt = -1.05 * Math.PI / 180.0;

    this.timeInterval = 300;
    this.layer = new NightshadeLayer(this);
    GTileLayerOverlay.call(this, this.layer);
}

NightshadeOverlay.prototype = new GTileLayerOverlay();
NightshadeOverlay.prototype.constructor = NightshadeOverlay;

NightshadeOverlay.prototype.stopTimer = function() {
    if (this.intervalRunning) {
        clearInterval(this.intervalRunning);
    }
};

NightshadeOverlay.prototype.startTimer = function() {
    this.stopTimer();

    this.layer.setCurrentTime();

    var tilelayeroverlay = this;

    this.intervalRunning = setInterval(function () { tilelayeroverlay.layer.setCurrentTime(); tilelayeroverlay.layer.mousemoved += 1; tilelayeroverlay.refresh(); }, this.timeInterval * 1000);
    //this.intervalRunning = setInterval(function () { tilelayeroverlay.layer.setCurrentTime(); tilelayeroverlay.refresh(); }, this.timeInterval * 1000);
};

NightshadeOverlay.prototype.remove = function() {
    this.stopTimer();
    GTileLayerOverlay.prototype.remove.call(this);
    var i;
    for (i in this.listeners) {
        GEvent.removeListener(this.listeners[i]);
    }
}

NightshadeOverlay.prototype.hideLights = function() {
    this.layer.wantShowLights = false;
    this.refresh();
}

NightshadeOverlay.prototype.showLights = function() {
    this.layer.wantShowLights = true;
    this.refresh();
}

NightshadeOverlay.prototype.isHiddenLights = function() {
    return !this.layer.wantShowLights;
}

NightshadeOverlay.prototype.initialize = function(map) {
    this.nso_map = map;
    GTileLayerOverlay.prototype.initialize.call(this, map);

    // setup interesting events from map here
    var tilelayeroverlay = this;
    this.listeners = [];
    this.listeners.push(GEvent.addListener(map, "zoomstart", function () { tilelayeroverlay.startTimer(); }));
    this.listeners.push(GEvent.addListener(map, "maptypechanged", function () { tilelayeroverlay.startTimer(); }));
    this.listeners.push(GEvent.addListener(map, "mousemove", function () { tilelayeroverlay.layer.mousemoved = 0; if (tilelayeroverlay.layer.wantShowLights && !tilelayeroverlay.layer.actuallyShowingLights) { tilelayeroverlay.refresh(); } }));
    this.startTimer();


}

