function hasImageCompleted(img) {
    var comp = img.complete;
    
    if (typeof comp == 'undefined') {        
        var test = new Image();
        test.src = img.src;
        comp = test.complete;
    }
    
    var retVal;
    
    if (comp === false) {
        retVal = false;
    } else if (comp === true && this.naturalWidth == 0) {       
        retVal = false;
    } else {
        retVal = true;
    }
    
    return retVal;
};

var ImageStackFlipper = Class.create({

    initialize: function(div, forward, back, flipCompletedHandler) {
        this.flipperDiv = $(div);
        this.forwardButton = $(forward);
        this.backButton = $(back);
        this.flipCompletedHandler = flipCompletedHandler;

        this.flipForwardEvent = this.flipForward.bindAsEventListener(this);
        this.flipBackwardEvent = this.flipBackward.bindAsEventListener(this);

        this.zIndexMultiplier = 20;  

        this.reset();
    },
  
    reset: function() {
        this.resetStacks();

        var divOffset = this.flipperDiv.positionedOffset();
        var divDimensions = this.flipperDiv.getDimensions();
        this.calculatePagePositions(divOffset, divDimensions);

        this.leftPageStack.each(function(page, i) {
            page.show();
            this.calculatePageSizes(page, divOffset, divDimensions);
            this.calculatePageOffsets(page);

            page.setStyle({ left: page.leftStackOffset.left + 'px',
                            top: page.leftStackOffset.top + 'px',
                            width: page.stackSize.width + 'px',
                            height: page.stackSize.height + 'px',
                            zIndex: (i + 1) * this.zIndexMultiplier });
        }.bind(this));

        this.flipForward();
    },
  
    resetStacks: function() {
        this.leftPageStack = [];
        this.selectedPage = null;
        this.rightPageStack = [];

        this.flipperDiv.select('img').reverse().each(function(page) {
            var p = $(page);

            if (!p.src.blank() && hasImageCompleted(p)) {
                if (!p.originalSize) {
                    p.originalSize = p.getDimensions();
                }

                this.leftPageStack.push(p);
            }
        }.bind(this));
    },
  
    calculatePagePositions: function(divOffset, divDimensions) {
        function createPosition(widthFactor, heightFactor) {
            return { left: divOffset.left + divDimensions.width * widthFactor, 
                     top: divOffset.top + divDimensions.height * heightFactor };
        }

        this.leftStackCenter = createPosition(0.125, 0.5);
        this.selectedCenter = createPosition(0.5, 0.5);
        this.rightStackCenter = createPosition(0.875, 0.5);
    },
  
    calculatePageSizes: function(page, divOffset, divDimensions) {
        function scaleToSmallerDimension(original, target) {
            var percent = { width: target.width / original.width,
                            height: target.height / original.height };

            var scale = percent.width < percent.height ? percent.width : percent.height;

            return { width: parseInt(original.width * scale), 
                     height: parseInt(original.height * scale) };
        }

        var maxStackSize = { width: divDimensions.width * 0.15,
                             height: divDimensions.height * 0.2 };
                         
        page.stackSize = scaleToSmallerDimension(page.originalSize, maxStackSize);

        var maxSelectedSize = { width: divDimensions.width * 0.5,
                                height: divDimensions.height * 0.9 };
                            
        page.selectedSize = scaleToSmallerDimension(page.originalSize, maxSelectedSize);
    },
  
    calculatePageOffsets: function(page) {
        function createOffset(pos, size) {
            return { left: parseInt(pos.left - size.width * 0.5),
                     top: parseInt(pos.top - size.height * 0.5) };
        }
        
        page.leftStackOffset = createOffset(this.leftStackCenter, page.stackSize);
        page.selectedOffset = createOffset(this.selectedCenter, page.selectedSize);
        page.rightStackOffset = createOffset(this.rightStackCenter, page.stackSize);
    },
  
    flipForward: function(e) {
        this.flip(e, 'forward');
    },
  
    flipBackward: function(e) {
        this.flip(e, 'backward');
    },

    flip: function(e, direction) {
        this.disableBothStacksClick();
        var fromStack = direction == 'forward' ? this.leftPageStack : this.rightPageStack;
        var toStack = direction == 'forward' ? this.rightPageStack : this.leftPageStack;
        var page = fromStack.pop();

        if (page) {
            var effects = [];
            effects.push(this.createSlideEffects(page, 'center'));

            if (this.selectedPage) {
                this.selectedPage.style.zIndex = (toStack.size() + 1) * this.zIndexMultiplier;
                
                effects.push(this.createSlideEffects(this.selectedPage, 
                                                     direction == 'forward' ? 'right' : 'left'));
                
                toStack.push(this.selectedPage);
            }

            this.launchEffects(effects, page);
        } else {
            this.enableBothStacksClick();
        }

        if (e) {
            Event.stop(e);
        }
    },
    
    launchEffects: function(effects, page) {
        var handleFinish = function() {
            this.selectedPage = page;
            this.enableBothStacksClick();

            if (this.flipCompletedHandler) {
                this.flipCompletedHandler(page);
            }
        }
    
        new Effect.Parallel(effects, { afterFinish: handleFinish.bind(this) });
    },
  
    createSlideEffects: function(page, position) {
        var effectSize;
        var effectPosition;

        switch (position) {
            case 'left':
                effectSize = page.stackSize;
                effectPosition = page.leftStackOffset
                break;

            case 'center':
                effectSize = page.selectedSize;
                effectPosition = page.selectedOffset
                break;

            case 'right':
                effectSize = page.stackSize;
                effectPosition = page.rightStackOffset
                break;
        }

        var sizeStyle = new Template('width: #{width}px; height: #{height}px;').evaluate(effectSize);
        var posStyle = new Template('left: #{left}px; top: #{top}px;').evaluate(effectPosition);

        return new Effect.Morph(page, { sync: true, style: sizeStyle + posStyle });
    },
  
    disableBothStacksClick: function() {
        this.forwardButton.stopObserving('click', this.flipForwardEvent);
        this.backButton.stopObserving('click', this.flipBackwardEvent);
    },

    enableBothStacksClick: function() { 
        this.forwardButton.observe('click', this.flipForwardEvent);
        this.backButton.observe('click', this.flipBackwardEvent);
    }

});

/****************************** START GOOGLE MAPS API ************************************/
var GoogleMapManager = Class.create({
    initialize: function(mapElementId) {
        Event.observe(window, 'unload', GUnload);

        this.map = new GMap2($(mapElementId));
        this.map.addControl(new GSmallMapControl());
        this.map.addControl(new GMenuMapTypeControl());
        this.map.setCenter(new GLatLng(0, 0), 0);
        
        this.geocoder = new GClientGeocoder();
        
        this.baseIcon = new GIcon();
        this.baseIcon.iconSize = new GSize(18, 18);
        this.baseIcon.iconAnchor = new GPoint(9, 34);
        this.baseIcon.infoWindowAnchor = new GPoint(9, 9);
        
    },
    
    getMap: function() {
        return this.map;
    },
    
    setCenter: function(coords) {
        this.map.setCenter(new GLatLng(coords.lat, coords.lng), 13);
    },

    showLocations: function(locations, locationPopupHtmlCallback) {
        this.map.clearOverlays();
        
        if (locations.size() > 0) {
            var pointBounds = new GLatLngBounds();

            locations.each(function(loc) {
                var point = new GLatLng(loc.lat, loc.lng);
                pointBounds.extend(point);
                var marker = new GMarker(point);

                if (locationPopupHtmlCallback) {
                    GEvent.addListener(marker, "click", function() {
                        marker.openInfoWindowHtml(locationPopupHtmlCallback(loc));
                    });
                }
                
                this.map.addOverlay(marker);
            }.bind(this));

            this.map.setCenter(pointBounds.getCenter(),
                               this.map.getBoundsZoomLevel(pointBounds));
        }
    },
    
    showNumberedLocations: function(locations, locationPopupHtmlCallback) {
        this.map.clearOverlays();
        
        if (locations.size() > 0) {
            var pointBounds = new GLatLngBounds();

            locations.each(function(loc) {
                var point = new GLatLng(loc.lat, loc.lng);
                pointBounds.extend(point);
                
                var numberedIcon = new GIcon(this.baseIcon);
                numberedIcon.image = "/images/mappins/pin_" + loc.number + ".gif";
                
                var marker = new GMarker(point, { icon : numberedIcon });

                if (locationPopupHtmlCallback) {
                    GEvent.addListener(marker, "click", function() {
                        marker.openInfoWindowHtml(locationPopupHtmlCallback(loc));
                    });
                }
                
                this.map.addOverlay(marker);
            }.bind(this));

            this.map.setCenter(pointBounds.getCenter(),
                               this.map.getBoundsZoomLevel(pointBounds));
        }
    },
    
    clearLocations: function() {
        this.map.clearOverlays();
    },
    
    showAddress: function(address, latLng, popupHtmlCallback, latLngCallback) {
        var showMapLocation = function(point) {
            if (point) {
                this.map.setCenter(point, 13);
                var marker = new GMarker(point);
                this.map.clearOverlays();
                this.map.addOverlay(marker);
                
                if (popupHtmlCallback) {
                    marker.openInfoWindowHtml(popupHtmlCallback());
                }
                
                if (latLngCallback) {
                    latLngCallback(point);
                }
            } else {
                this.map.setCenter(new GLatLng(41, -92), 3);
            }
        }.bind(this);
    
        if (latLng) {
            showMapLocation(new GLatLng(latLng.lat, latLng.lng));
        } else {
            this.geocoder.getLatLng(storeAddress, showMapLocation);
        }
    }
});

var GoogleDirectionsManager = Class.create({
    initialize: function(mapManager, directionsElementId) {
        this.map = mapManager.getMap();
        this.directions = new GDirections(this.map, $(directionsElementId));
        
        GEvent.addListener(this.directions, 'error', function() {
            
            if(this.directions)
            {
                var status = this.directions.getStatus();
                var errorMessage = "An error has occurred while attempting to retrieve directions!";
                
                if (status.code == G_GEO_UNKNOWN_ADDRESS) {
                    errorMessage = "We couldn't understand this location";
                }
            }
            else
            {
                errorMessage = "We couldn't understand this location";
            }
            alert(errorMessage);
        });
    },
    
    getDirections: function(startAddress, endAddress) {
        this.map.clearOverlays();
        
        this.directions.load("from: " + startAddress + " to: " + endAddress, 
                             { locale: "en_US" });
    }
});
/****************************** END GOOGLE MAPS API **************************************/