OpenLayers.Layer.SimpleGrid = OpenLayers.Class.create();
OpenLayers.Layer.SimpleGrid.prototype = OpenLayers.Class.inherit(OpenLayers.Layer, {
    tileSize: null,
    grid: null,
    buffer: 1,
    initialize: function(name, options) {
        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
        this.isMoving = false;
        this.grid = new Array();
    },
    destroy: function() {
        this.clearGrid();
        this.grid = null;
        this.tileSize = null;
    },
    clone: function(obj) {
        gexD.pl("SimpleGrid.clone(" + this.name + ")");
        if (obj == null) {
            obj = new OpenLayers.Layer.Grid(this.name, this.options);
        }
        if (this.tileSize != null) {
            obj.tileSize = this.tileSize.clone();
        }
        obj.grid = null;
        return obj;
    },
    setMap: function(map) {
        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
        if (this.tileSize == null) {
            this.tileSize = map.getTileSize();
        }
    },
    getGridOriginPositionIndex: function() {
        if (Gex.isNotUndef(this.grid[0]) && Gex.isNotUndef(this.grid[0][0])) {
            var t = this.grid[0][0];
            return {
                position: t.position,
                index: t.bounds.getTileIndex()
            };
        }
        var gc = this.getGridCells();
        if (this.map.zoom <= 0) {
            return null;
        }
        var layerContOff = this._initGrid();
        var gexPos = gc.getCellPosition();
        var bound = gc.getCellBounds();
        var pos = new OpenLayers.Pixel(gexPos.x + layerContOff.x, gexPos.y + layerContOff.y);
        return {
            position: pos,
            index: bound.getTileIndex()
        };
    },
    moveTo: function(bounds, zoomChanged, dragging) {
        if (this.isMoving) {
            return;
        }
        this.isMoving = true;
        if (bounds == null) {
            bounds = this.map.getExtent();
        }
        if (bounds != null) {
            if (!this.grid.length || zoomChanged || !this.getGridBounds().containsBounds(bounds, true)) {
                this._initTiles();
            } else {
                while (true) {
                    var origin = this.getGridOriginPositionIndex();
                    var ts = this.tileSize;
                    var b = this.buffer;
                    var tlPos = this.map.getViewPortPxFromLayerPx(origin.position);
                    if (tlPos.x > -ts.w * b) {
                        this.shiftColumn(true);
                    } else if (tlPos.x < -ts.w * (b + 1)) {
                        this.shiftColumn(false);
                    } else if (tlPos.y > -ts.h * b) {
                        this.shiftRow(true);
                    } else if (tlPos.y < -ts.h * (b + 1)) {
                        this.shiftRow(false);
                    } else {
                        break;
                    }
                }
                if (this.buffer == 0) {
                    for (var r = 0, rl = this.grid.length; r < rl; r++) {
                        var row = this.grid[r];
                        for (var c = 0, cl = row.length; c < cl; c++) {
                            var tile = row[c];
                            if (!tile.drawn && tile.bounds.intersectsBounds(bounds, false)) {
                                tile.draw();
                            }
                        }
                    }
                }
            }
        }
        this.isMoving = false;
    },
    getGridBounds: function() {
        throw "Not Implemented";
    },
    getGridCells: function() {
        throw "Not Implemented";
    },
    _initGrid: function() {
        var gc = this.getGridCells();
        gc.reset();
        var viewPortOff = gc.getCellsOffset();
        return viewPortOff;
    },
    _initTiles: function() {
        var layerContOff = this._initGrid();
        var gc = this.getGridCells();
        var gs = gc.getGridSize();
        var rowIdx;
        var colIdx;
        for (rowIdx = 0; rowIdx < gs.h; rowIdx++) {
            var row = this.grid[rowIdx];
            if (!row) {
                row = [];
                this.grid.push(row);
            }
            for (colIdx = 0; colIdx < gs.w; colIdx++) {
                var gexPos = gc.getCellPosition();
                var bound = gc.getCellBounds();
                var pos = new OpenLayers.Pixel(gexPos.x + layerContOff.x, gexPos.y + layerContOff.y);
                var tile = row[colIdx];
                if (!tile) {
                    tile = gc.createNewTile(bound, pos);
                    row.push(tile);
                }
                else {
                    this.isValidTile(bound.getTileIndex());
                    tile.moveTo(bound, pos, false);
                }
                gc.incrementCol();
            }
            gc.incrementRow();
        }
        while (this.grid.length > rowIdx) {
            var row = this.grid.pop();
            for (var i = 0, l = row.length; i < l; i++) {
                row[i].destroy();
            }
        }
        while (this.grid[0].length > colIdx) {
            for (var i = 0, l = this.grid.length; i < l; i++) {
                var row = this.grid[i];
                var tile = row.pop();
                tile.destroy();
            }
        }
        this.spiralTileLoad();
    },
    spiralTileLoad: function() {
        var tileQueue = new Array();
        var directions = ["right", "down", "left", "up"];
        var iRow = OpenLayers.Util.indexOf(directions, "right");
        var iCell = -1;
        var direction = 0;
        var directionsTried = 0;
        while (directionsTried < directions.length) {
            var testRow = iRow;
            var testCell = iCell;
            switch (directions[direction]) {
            case "right":
                testCell++;
                break;
            case "down":
                testRow++;
                break;
            case "left":
                testCell--;
                break;
            case "up":
                testRow--;
                break;
            }
            var tile = null;
            if ((testRow < this.grid.length) && (testRow >= 0) && (testCell < this.grid[0].length) && (testCell >= 0)) {
                tile = this.grid[testRow][testCell];
            }
            if ((tile != null) && (!tile.queued)) {
                tileQueue.unshift(tile);
                tile.queued = true;
                directionsTried = 0;
                iRow = testRow;
                iCell = testCell;
            } else {
                direction = (direction + 1) % 4;
                directionsTried++;
            }
        }
        for (var i = 0; i < tileQueue.length; i++) {
            var tile = tileQueue[i];
            if (this.isValidTile(tile)) {
                tile.draw();
            }
            tile.queued = false;
        }
    },
    clearGrid: function() {
        if (this.grid) {
            for (var iRow = 0; iRow < this.grid.length; iRow++) {
                var row = this.grid[iRow];
                for (var iCol = 0; iCol < row.length; iCol++) {
                    row[iCol].destroy();
                }
            }
            this.grid = [];
        }
    },
    isValidTile: function(tile) {
        return true;
    },
    convertTile: function(tile, adjacentTile, horiIdx, vertIdx) {
        throw "Not Implemented";
    },
    shiftRow: function(prepend) {
        var modelRowIndex = (prepend) ? 0: (this.grid.length - 1);
        var modelRow = this.grid[modelRowIndex];
        var row = (prepend) ? this.grid.pop() : this.grid.shift();
        var preRow = row[0].bounds.getTileIndex().y;
        for (var i = 0; i < modelRow.length; i++) {
            var modelTile = modelRow[i];
            this.convertTile(row[i], modelTile, 0, (prepend ? -1: 1));
        }
        var newRow = row[0].bounds.getTileIndex().y;
        if (prepend) {
            this.grid.unshift(row);
        } else {
            this.grid.push(row);
        }
        gexD.pl("ShiftRow " + preRow + " => " + newRow);
    },
    shiftColumn: function(prepend) {
        var preCol = 0;
        var newCol = 0;
        for (var i = 0; i < this.grid.length; i++) {
            var row = this.grid[i];
            var modelTileIndex = (prepend) ? 0: (row.length - 1);
            var modelTile = row[modelTileIndex];
            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift()
            preCol = tile.bounds.getTileIndex().x;
            this.convertTile(tile, modelTile, (prepend ? -1: 1), 0);
            if (prepend) {
                this.grid[i].unshift(tile);
            } else {
                this.grid[i].push(tile);
            }
            newCol = tile.bounds.getTileIndex().x;
        }
        gexD.pl("ShiftColumn " + preCol + " => " + newCol);
    },
    reloadLayer: function() {},
    CLASS_NAME: "OpenLayers.Layer.SimpleGrid"
});
OpenLayers.Layer.SimpleGrid.GridCells = OpenLayers.Class.create();
OpenLayers.Layer.SimpleGrid.GridCells.prototype = {
    initialize: function() {},
    incrementRow: function() {},
    incrementCol: function() {},
    reset: function() {
        throw "Not Implemented";
    },
    getGridSize: function() {
        throw "Not Implemented";
    },
    getCellBounds: function() {
        throw "Not Implemented";
    },
    getCellPosition: function() {
        throw "Not Implemented";
    },
    createNewTile: function(bound, pos) {
        throw "Not Implemented";
    },
    getCellsOffset: function() {
        throw "Not Implemented";
    },
    getZoom: function() {
        throw "Not Implemented";
    },
    CLASS_NAME: "OpenLayers.Layer.SimpleGrid.GridCells"
};
OpenLayers.Util.onImageLoadError = function() {
    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
    if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
        this.src = this.src;
    } else {
        this.src = "images/noImage.gif";
    }
    this.style.display = "";
}
OpenLayers.Layer.DigitalGlobe = OpenLayers.Class.create();
OpenLayers.Layer.DigitalGlobe.prototype = OpenLayers.Class.inherit(OpenLayers.Layer.SimpleGrid, { //OpenLayers.Layer.FixedZoomLevels, {
    orderCount: 0,
    order: 0,
    prov: null,
    gc: null,
    overlay: null,
    MIN_ZOOM_LEVEL: 0,
    MAX_ZOOM_LEVEL: 19,
    ls: 4,
    imagery: null,
    defaultProj: new Gex.ProjectionLatLon(),
    displayOutsideMaxExtent: true,
    initialize: function(name, proj, options) {
        OpenLayers.Layer.SimpleGrid.prototype.initialize.apply(this, [name, options]);
        if (this.maxExtent == null) {
            this.maxExtent = new OpenLayers.Bounds( - 360, -90, 360, 90);
        }
        this.minZoomLevel = this.MIN_ZOOM_LEVEL;
        this.maxZoomLevel = this.MAX_ZOOM_LEVEL;
        if (!Gex.isNotUndef(this.options.numZoomLevels)) {
            this.options.numZoomLevels = this.MAX_ZOOM_LEVEL - this.MIN_ZOOM_LEVEL + 1;
        }
        if (this.overlay == null) {
            this.isBaseLayer = true;
            this.imagery = new Gex.AerialImagery(this.ls);
        }
        else {
            this.isBaseLayer = false;
            this.imagery = new Gex.OverlayImagery(this.overlay);
            this.alpha = OpenLayers.Util.alphaHack();
        }
        var p = proj || this.defaultProj;
        this.prov = new Gex.TilesProvider(p, this.imagery);
        var tSize = this.prov.getTileSize();
        this.tileSize = new OpenLayers.Size(tSize, tSize);
        if (this.overlay != null) {
            this.setVisibility(false, true);
        }
        this.tpoint = new Gex.Point();
        this.order = OpenLayers.Layer.DigitalGlobe.prototype.order++;
        gexD.pl("DigitalGlobe.initialized: " + this.order);
    },
    clone: function() {
        var cl = new OpenLayers.Layer.DigitalGlobe(this.name, this.proj)
        cl.ls = this.ls;
        cl.overlay = this.overlay;
        cl.MIN_ZOOM_LEVEL = this.MIN_ZOOM_LEVEL;
        cl.MAX_ZOOM_LEVEL = this.MAX_ZOOM_LEVEL;
        return cl;
    },
    destroy: function() {
        this.prov = null;
        this.gc = null;
        this.imagery = null;
        if (this.overlay != null && this.map != null) {
            this.unregisterProjectionChangeEvent(this.map);
        }
        OpenLayers.Layer.SimpleGrid.prototype.destroy.apply(this, arguments);
    },
    registerProjectionChangeEvent: function(map) {
        map.events.register("changebaselayer", this, this.onProjectionChange);
    },
    unregisterProjectionChangeEvent: function(map) {
        map.events.unregister("changebaselayer", this, this.onProjectionChange);
    },
    setMap: function(map) {
        if (this.overlay != null) {
            if (this.map != null) {
                this.unregisterProjectionChangeEvent(this.map);
            }
            this.registerProjectionChangeEvent(map);
        }
        OpenLayers.Layer.SimpleGrid.prototype.setMap.apply(this, arguments);
    },
    onMapResize: function() {},
    getGexBaseLayer: function() {
        if (this.map.baseLayer.CLASS_NAME == OpenLayers.Layer.DigitalGlobe.prototype.CLASS_NAME) {
            return this.map.baseLayer;
        }
        return null;
    },
    onProjectionChange: function() {
        var baseLayer = this.map.baseLayer;
        if (Gex.isNotUndef(baseLayer.prov)) {
            var newProj = baseLayer.prov.getProjection();
            var curProj = this.prov.getProjection();
            if (newProj.getName() != curProj.getName()) {
                this.prov.setProjection(newProj);
                if (this.getVisibility()) {
                    window.setTimeout(this.moveTo.bind(this, null, true), 1);
                }
            }
        }
        return true;
    },
    getURL: function(bound, zoom) {
        var index = bound.getTileIndex();
        var z = zoom || this.getGexZoomFromOLZoom(this.map.getZoom());
        return this.prov.getUrl(index, z);
    },
    getGridBounds: function() {
        var topLeftTile = this.grid[0][0];
        var h = this.grid.length;
        var w = this.grid[0].length;
        var tIdx = topLeftTile.bounds.getTileIndex();
        var proj = this.prov.getProjection();
        var zoom = this.getGexZoomFromOLZoom(this.map.getZoom());
        var tileSize = this.prov.getTileSize();
        var t = this.tpoint;
        t.copy(tIdx);
        var topLeftPoint = new Gex.Point(t.x * tileSize, t.y * tileSize);
        t.x = tIdx.x + w;
        t.y = tIdx.y + h;
        var botRighPoint = new Gex.Point(t.x * tileSize, t.y * tileSize);
        var topLeftCoord = proj.pixelToDegree(topLeftPoint, zoom, this.prov);
        var botRighCoord = proj.pixelToDegree(botRighPoint, zoom, this.prov);
        var b = new OpenLayers.Bounds(topLeftCoord.lon, botRighCoord.lat, botRighCoord.lon, topLeftCoord.lat);
        return b;
    },
    getGridCells: function() {
        if (this.gc == null) {
            this.gc = new OpenLayers.Layer.DigitalGlobe.GridCells(this);
        }
        return this.gc;
    },
    convertTile: function(tile, adjacentTile, horiIdx, vertIdx) {
        var atIdx = adjacentTile.bounds.getTileIndex();
        var tileIndex = tile.bounds.getTileIndex();
        tileIndex.x = atIdx.x + horiIdx,
        tileIndex.y = atIdx.y + vertIdx;
        var tileSize = this.prov.getTileSize();
        var atPos = adjacentTile.position;
        var tPos = atPos.add(horiIdx * tileSize, vertIdx * tileSize);
        var redraw = true;
        if (!this.isValidTile(tileIndex)) {
            redraw = false;
        }
        tile.moveTo(tile.bounds, tPos, redraw);
    },
    isValidTile: function(tile) {
        var t = tile;
        if (Gex.isNotUndef(tile.bounds) && Gex.isNotUndef(tile.bounds.getTileIndex)) {
            t = tile.bounds.getTileIndex();
        }
        var gc = this.getGridCells();
        var z = gc.getZoom();
        var ok = this.prov.isValidTile(t, z);
        return ok;
    },
    setVisibility: function(visible, noEvent) {
        if (this.overlay && visible) {
            var prov = this.prov;
            var gexBaseLayer = this.getGexBaseLayer();
            if (gexBaseLayer != null) {
                var newProj = gexBaseLayer.prov.getProjection();
                if (prov.getProjection() != newProj) {
                    prov.setProjection(newProj);
                }
            }
        }
        OpenLayers.Layer.SimpleGrid.prototype.setVisibility.call(this, visible, noEvent);
    },
    moveTo: function(bounds, zoomChanged, dragging) {
        var display = this.visibility;
        if (!this.isBaseLayer) {
            display = display && this.inRange;
        }
        this.display(display);
        if (zoomChanged) {
            var zoom = this.map.getZoom();
            if (zoom < this.minZoomLevel || zoom > this.maxZoomLevel) {
                this.setVisibility(false);
            }
        }
        OpenLayers.Layer.SimpleGrid.prototype.moveTo.call(this, bounds, zoomChanged, dragging);
    },
    getLonLatFromViewPortPx: function(viewPortPx) {
        var lonlat = null;
        var origin = this.getGridOriginPositionIndex();
        if (this.prov != null && origin != null) {
            var proj = this.prov.getProjection();
            var zoom = this.getGexZoomFromOLZoom(this.map.getZoom());
            var tSize = this.prov.getTileSize();
            var origTilePos = origin.position;
            var origTileIdx = origin.index;
            var p = this.map.getLayerPxFromViewPortPx(viewPortPx);
            p.x = p.x - origTilePos.x + (tSize * origTileIdx.x);
            p.y = p.y - origTilePos.y + (tSize * origTileIdx.y);
            var coord = proj.pixelToDegree(p, zoom, this.prov);
            lonlat = coord;
        }
        return lonlat;
    },
    getViewPortPxFromLonLat: function(lonlat) {
        var viewPortPx = null;
        var origin = this.getGridOriginPositionIndex();
        if (this.prov != null && origin != null) {
            var coord = this.getCoordinateFromOLLonLat(lonlat);
            var proj = this.prov.getProjection();
            var zoom = this.getGexZoomFromOLZoom(this.map.getZoom());
            var gexPoint = proj.degreeToPixel(coord, zoom, this.prov);
            var gexOrigTileIdx = origin.index;
            var tSize = this.prov.getTileSize();
            var gexOrigTilePos = new Gex.Point(tSize * gexOrigTileIdx.x, tSize * gexOrigTileIdx.y);
            var offsetPix = gexPoint.add( - gexOrigTilePos.x, -gexOrigTilePos.y);
            var origTilePos = origin.position;
            var layerContPix = origTilePos.offset(offsetPix);
            viewPortPx = this.map.getViewPortPxFromLayerPx(layerContPix);
        }
        return viewPortPx;
    },
    getZoomForExtent: function(bounds) {
        var zoom = null;
        if (this.prov != null) {
            var grid = new Gex.TileGrid(this.prov);
            var viewSize = this.getGexSizeFromOLSize(this.map.getSize());
            grid.setViewportSize(viewSize);
            var coordBound = bounds;
            var gexZoom = grid.getZoomFromBound(coordBound);
            var gexZoom = Math.min(Math.max(gexZoom, this.minZoomLevel), this.maxZoomLevel);
            zoom = this.getOLZoomFromGexZoom(gexZoom);
        }
        return zoom;
    },
    calculateInRange: function() {
        if (this.map) {
            var curZoom = this.map.getZoom();
            if (this.minZoomLevel <= curZoom && curZoom <= this.maxZoomLevel) {
                return true;
            }
        }
        return false
    },
    getOLZoomFromGexZoom: function(gexZoom) {
        return gexZoom;
    },
    getGexZoomFromOLZoom: function(olZoom) {
        return olZoom;
    },
    getOLLonLatFromCoordinate: function(coord) {
        return coord;
    },
    getCoordinateFromOLLonLat: function(olLonLat) {
        return olLonLat;
    },
    getOLPixelFromGexPoint: function(gexPoint) {
        return gexPoint;
    },
    getGexPointFromOLPixel: function(olPixel) {
        return olPixel;
    },
    getOLSizeFromGexSize: function(gexSize) {
        return gexSize;
    },
    getGexSizeFromOLSize: function(olSize) {
        return olSize;
    },
    getOLBoundsFromCoordinateBound: function(coordBound) {
        return coordBounds;
    },
    getCoordinateBoundFromOLBounds: function(olBounds) {
        return olBounds;
    },
    CLASS_NAME: "OpenLayers.Layer.DigitalGlobe"
});
OpenLayers.Layer.DigitalGlobe.Bounds = OpenLayers.Class.create();
OpenLayers.Layer.DigitalGlobe.Bounds.prototype = OpenLayers.Class.inherit(OpenLayers.Bounds, {
    bypassCheck: true,
    tileIndex: null,
    initialize: function(name, options) {
        OpenLayers.Bounds.prototype.initialize.apply(this, arguments);
    },
    setTileIndex: function(foo) {
        this.tileIndex = foo;
    },
    getTileIndex: function() {
        return this.tileIndex;
    },
    clone: function() {
        var retVal = new OpenLayers.Layer.DigitalGlobe.Bounds(this.left, this.bottom, this.right, this.top);
        retVal.tileIndex = this.tileIndex.clone();
        return retVal;
    }
});
OpenLayers.Layer.DigitalGlobe.GridCells = OpenLayers.Class.create();
OpenLayers.Layer.DigitalGlobe.GridCells.prototype = OpenLayers.Class.inherit(OpenLayers.Layer.SimpleGrid.GridCells, {
    initialize: function(layer) {
        this.layer = layer
        this.tg = new Gex.TileGrid(layer.prov);
        this.tg.setBuffer(new Gex.Size(layer.buffer, layer.buffer));
    },
    incrementRow: function() {},
    incrementCol: function() {
        this.cellIdx++;
    },
    reset: function() {
        var l = this.layer;
        var tg = this.tg;
        var viewSize = l.map.getSize();
        if (!viewSize.equals(tg.getViewportSize())) {
            tg.setViewportSize(viewSize);
        }
        var gexCenter = l.map.getCenter();
        var zoom = l.map.getZoom();
        tg.setCenter(gexCenter, zoom);
        this.cells = tg.getCells();
        this.cellIdx = 0;
        gexD.pl("FIRST=" + this.cells[0].getTileIdx().toString());
    },
    getGridSize: function() {
        return this.tg.getGridSize();
    },
    getCellIndex: function() {
        var c = this.cells[this.cellIdx];
        return c.getTileIdx();
    },
    getCellBounds: function() {
        var index = this.getCellIndex();
        var tileSize = this.layer.prov.getTileSize();
        var tileTopLeftPix = new Gex.Point(index.x * tileSize, index.y * tileSize);
        var tileLowerRightPix = tileTopLeftPix.add(tileSize, tileSize);
        var zoom = this.getZoom();
        var proj = this.layer.prov.getProjection();
        var tileTopLeftLatLon = proj.pixelToDegree(tileTopLeftPix, zoom, this.layer.prov);
        var tileLowerRightLatLon = proj.pixelToDegree(tileLowerRightPix, zoom, this.layer.prov);
        var bounds = new OpenLayers.Layer.DigitalGlobe.Bounds(tileTopLeftLatLon.lon, tileLowerRightLatLon.lat, tileLowerRightLatLon.lon, tileTopLeftLatLon.lat);
        bounds.setTileIndex(index);
        return bounds;
    },
    getCellPosition: function() {
        var c = this.cells[this.cellIdx];
        return c.getTilePos();
    },
    createNewTile: function(bound, pos) {
        var l = this.layer;
        var url = l.getURL(bound, this.tg.getZoom());
        var img = new OpenLayers.Tile.Image(l, pos, bound, url, l.tileSize);
        return img;
    },
    getCellsOffset: function() {
        return this.tg.getTileOffset();
    },
    getZoom: function() {
        return this.tg.getZoom();
    },
    CLASS_NAME: "OpenLayers.Layer.DigitalGlobe.GridCells"
});