var SearchMap = new Singleton(function($) {
    var klass = this;
    this.canvas;
    
    this.methods({
        initialize: function(latitude, longitude, zoom) {
            klass.canvas = new GMap2($('#google_map').get(0));
            klass.canvas.setCenter(new GLatLng(latitude, longitude), zoom);
            klass.canvas.addControl(new GMapTypeControl());
            klass.canvas.removeMapType(G_HYBRID_MAP);
            klass.canvas.addMapType(G_PHYSICAL_MAP);
            klass.canvas.addControl(new GLargeMapControl());
            
            klass.street_view.initialize();
        },
        resize: function() {
            klass.canvas.checkResize();
        },
        center: function(point) {
            klass.canvas.setCenter(point);
        }
    });
    
    this.namespace('properties', function() {
        var properties = this;
        this.items = {};
        this.current_listing;
    
        this.methods({
            hideActive: function() {
                klass.canvas.closeInfoWindow();
                for (var i = Search.active.length - 1; i >= 0; i--) {
                    var marker = properties.getMarker(Search.active[i].k);
                    if (marker) { marker.hide(); }
                };
            },
            popUpFirstActive: function() {
                var listing = Search.active[0];
                if (listing) {
                    SearchMap.center(new GLatLng(listing.l, listing.m));
                    SearchMap.properties.showPopUp(listing.k);
                }
            },
            getMarker: function(id) {
                return properties.items[id];
            },
            popupHtml: function(listing) {
                // Onclick events here because IE6 Sucks
                return ' \
                <div class="google_listing" style="width: 350px; height: 250px"> \
                    <div class="address"> \
                        <span class="street">'+Listing.formatTitle(listing.a)+'</span> \
                        <span class="city">'+listing.b+', '+listing.c+' '+listing.d+'</span> \
                    </div> \
                    <div class="image"><img alt="address" src="'+listing.j+'" style="width: 170px"/></div> \
                    <div class="basic_info"> \
                        '+Listing.formattedDetailsTable(Listing.basicDetails(listing))+' \
                        </div> \
                    <div class="links"> \
                        <a href="/searches/listings/'+listing.k+'" onclick="Search.listings.details(this.href); return false;" \
                            class="google_view_listing_details">View Details</a> | \
                        <a href="/searches/saved_listings?listing[real_estate_id]='+listing.k+'" onclick="Search.saveListing(this); return false;" \
                            class="google_save_listing">Add to Favorites</a> \
                    </div> \
                    <div class="courtesy_of">'+Listing.formatTitle(listing.n)+Listing.formatDataSourceIcon(listing.p)+'</div> \
                </div>';
            },
            tooltipHtml: function(listing) {
                return ' \
                <div class="toolip"> \
                    <div class="header"> \
                        <span class="price">'+Listing.formatCurrency(listing.e)+'</span> \
                        <span class="address">'+Listing.formatTitle(listing.a)+'</span> \
                    </div> \
                    <div class="details"> \
                        <span>'+listing.f+'/'+listing.g+' Bd/Ba - '+listing.h+' Sqft.</span> \
                    </div> \
                </div>';
            },
            showPopUp: function(id) {
                var marker = this.items[id];
                if (marker != null) { GEvent.trigger(marker, 'click'); }
            },
            show: function(listing) {
                var marker = this.items[listing.k];
                if (!marker) { marker = this.add(listing); }
                marker.show();
            },
            hide: function(listing) {
                this.items[listing.k].hide();
            },
            add: function(listing) {
                var point = new GLatLng(listing.l, listing.m);
                var marker = properties.createMarker(point, listing);
                klass.canvas.addOverlay(marker);
    
                this.items[listing.k] = marker;
                return marker;
            },
            createMarker: function(point, listing) {
                var icon = new GIcon();
                icon.iconAnchor = new GPoint(0, 0);
                icon.infoWindowAnchor = new GPoint(1, 1);
                icon.image = '/images/skins/default/house_icons/green_house_04.png';
                icon.iconSize = new GSize(35, 40); 
                icon.iconAnchor = new GPoint(17, 40);
                icon.infoWindowAnchor = new GPoint(7, 5);
            
                var marker = new GMarker(point, { icon: icon, hide: true });
    
                // Show this marker's index in the info window when it is clicked
                GEvent.addListener(marker, 'click', function(overlay, coords) {
                    SearchMap.properties.current_listing = listing;
                    marker.openInfoWindowHtml(properties.popupHtml(listing));
                });
    
                // Show this markers tooltip
                var tooltip = new Tooltip(marker, properties.tooltipHtml(listing), 4);
                marker.tooltip = tooltip;
                klass.canvas.addOverlay(tooltip);
                GEvent.addListener(marker, 'mouseover', function() { this.tooltip.show(); });
                GEvent.addListener(marker, 'mouseout', function() { this.tooltip.hide(); });
    
                return marker;
            }
        });
    });
    
    this.namespace('street_view', function() {
        var street_view = this;
        this.canvas;
        this.client;
        
        this.methods({
            initialize: function() {
                this.canvas = new GStreetviewPanorama($('#google_street_map').get(0));
                this.client = new GStreetviewClient();
            },
            noData: function() {
                alert('Sorry, but Google has not yet indexed this area.');
                $('#toggle_google_map a[href=#show_map_view]').click();
            },
            show: function() {
                var marker = SearchMap.properties.getMarker(SearchMap.properties.current_listing.k);
                street_view.client.getNearestPanorama(marker.getLatLng(), street_view.handleData);
            },
            update: function(listing, location) {
                function wrapAngle(angle) {
                    if (angle >= 360) {
                        angle -= 360;
                    } else if (angle < 0) {
                        angle += 360;
                    }
                    return angle;
                };
                
                function computeAngle(endLatLng, startLatLng) {
                    var DEGREE_PER_RADIAN = 57.2957795;
                    var RADIAN_PER_DEGREE = 0.017453;

                    var dlat = endLatLng.lat() - startLatLng.lat();
                    var dlng = endLatLng.lng() - startLatLng.lng();
                    // We multiply dlng with cos(endLat), since the two points are very closeby,
                    // so we assume their cos values are approximately equal.
                    var yaw = Math.atan2(dlng * Math.cos(endLatLng.lat() * RADIAN_PER_DEGREE), dlat)
                    * DEGREE_PER_RADIAN;
                    return wrapAngle(yaw);
                }
                
                var marker = SearchMap.properties.getMarker(listing.k);
                var angle = computeAngle(marker.getLatLng(), location.latlng);
                street_view.canvas.setLocationAndPOV(location.latlng, { yaw: angle });
            },
            handleData: function(result) {
                if (result.code != 200) { street_view.noData(); return; }
                street_view.update(SearchMap.properties.current_listing, result.location);
            }  
        });

    });
    
    this.namespace('polygon', function() {
        
        this.methods({
            // Based on: http://gmaps-samples.googlecode.com/svn/trunk/poly/mymapstoolbar.html
            extendGoogleAPI: function() {
                /*
                * Extension to the GPolygon object.
                * This is a method for testing if a point is inside a polygon
                * The origins of this function can be traced to the code posted at http://alienryderflex.com/polygon/
                * @param {GLatLng} point - the point of interest
                * @returns boolean - true if poly contains point
                */
                GPolygon.prototype.Contains = function(point) {
                    var j=0;
                    var func_oddNodes = false;
                    var x = point.lng();
                    var y = point.lat();
                    for (var i=0; i < this.getVertexCount(); i++) {
                        j++;
                        if (j == this.getVertexCount()) {j = 0;}
                        if (((this.getVertex(i).lat() < y) && (this.getVertex(j).lat() >= y)) || ((this.getVertex(j).lat() < y) && (this.getVertex(i).lat() >= y))) {
                            if ( this.getVertex(i).lng() + (y - this.getVertex(i).lat()) / (this.getVertex(j).lat()-this.getVertex(i).lat()) * (this.getVertex(j).lng() - this.getVertex(i).lng())<x ) {
                                func_oddNodes = !func_oddNodes;
                            }
                        }
                    }
                    return func_oddNodes;
                };
            },
            activateDrawing: function() {
                var color = '#000080';
                var name = 'polygon';
    
                function startDrawing(poly, name, onUpdate) {
                    map.addOverlay(poly);
                    poly.enableDrawing();
                    poly.enableEditing({onEvent: 'mouseover'});
                    poly.disableEditing({onEvent: 'mouseout'});
                    GEvent.addListener(poly, 'endline', function() {
                        GEvent.bind(poly, 'lineupdated', name, onUpdate);
                        GEvent.addListener(poly, 'click', function(latlng, index) {
                            if (typeof index == 'number') {
                                poly.deleteVertex(index);
                            } else {
                                poly.setStrokeStyle({color: color, weight: 4});
                            }
                        });
                    });
                }
    
                polygon = new GPolygon([], color, 2, 0.7, color, 0.2);
                startDrawing(polygon, name, function() {
                    var area = polygon.getArea();
                    Math.round(area/10000) / 100;
                });
            },
            checkPoints: function() {
                var markers = GoogleMaps.markers();
                if (markers.length > 0) {
                    for (var i = markers.length - 1; i >= 0; i--) {
                        if (polygon.Contains(markers[i].getLatLng())) {
                            markers[i].show();
                        }
                        else {
                            markers[i].hide();
                        }
                    };
                }
            }         
        });        
        this.extendGoogleAPI();
    });
});
