(function($){
    $.fn.extend({
        tweatMap: function(options) {
            var defaults = {
                considerGL : false,
                userCurrAddress: "",                
                checkGeolocationURL : '/user/checkGeolocation/',
                fetchResultsURL: '/restaurant/fetchDiscoverResults/',
                guessUserAddressURL: '/restaurant/guessUserAddress/',
                defaultRadius: 1,
                geolocationEnabled : false,                
                defaultZoomLevel : 13,
                userLocIcon: "http://maps.google.com/mapfiles/ms/micons/POI.png",
                userLocIconShadow: "http://maps.google.com/mapfiles/ms/micons/POI.shadow.png",
                radiusIcon: "http://maps.google.com/mapfiles/kml/pal4/icon57.png",
                radiusIconShadow: "http://maps.google.com/mapfiles/kml/pal4/icon57s.png",
                fetchResultsOnLoad: false,
                permissionTimeout: 3000,
                currentLocHolder: 'li.user-current-location',
                displayRadiusHolder :'li.coverage-radius span.radius-value',
                tagsHolder: '#tags_holder',
                glNotSupportedNotification:'#address_select_box',                
                addressInput: '#address_input_box',
                addressInputBtn: '#address_input_submit',
                radiusSelectSlider: '#radius_select_slider',
                additionalOptionsCallback: ''
                
            }
            var markerCounter = 0;
            var o = $.extend(defaults, options);
            $(o.glNotSupportedNotification).dialog({
                width: 400,
                height:300,
                modal: true,
                autoOpen: false,                
                buttons: {
                    OK: function(){
                        $(this).dialog('close');
                        guessUserAddress();
                    }
                }
            })

            var tooltip = document.createElement("div");
            tooltip.setAttribute("id", "toolTip");
            tooltip.setAttribute("class", "toolTip"); // we style the tooltip via the "toolTip" class
            tooltip.style.visibility = "hidden"; // we dont want to see it right now.

            var circle;
            var circleProp = {
                sColor : '#cc0000',
                fColor : '#cc0000',
                sWidth : 2,
                sOpacity : 0.3,
                fOpacity : 0.1,
                noOfPoints : 30
            };

            var allTags = new Array();
            var markerBounds = new GLatLngBounds();
                        
            var mapClickListener;
            var userCurrLatLng = new GLatLng(0,0);
            var userHasAddress = false;
            var radiusCurrPos = userCurrLatLng;
            var userCurrSearchRadius = o.defaultRadius;
            var browserSupportsGL = false;
            var userLocMarker;
            var radiusMarker;
            var markers = new Array();
            var removableOverlays = new Array();

            $('p.load-more').live('mouseover', function(){
                $(this).css('cursor', 'pointer')
                $(this).addClass("ui-state-hover");
            }).live('mouseout', function(){
                $(this).removeClass("ui-state-hover");
            }).live('mousedown', function(){
                $(this).addClass("ui-state-active");
            }).live('mouseup', function(){
                $(this).removeClass("ui-state-active");
            }).hide();

            $('li.show-in-map').live('click', function(){
                $.fn.triggerMarker($(this).attr('rel'));
            });

            $('#map_canvas').localScroll({
                lazy: true,
                onAfter: animateRestaurant
            });
            
            //get data from session
            var map = new GMap2(document.getElementById($(this).attr('id')));
            map.setUIToDefault();
            map.disableScrollWheelZoom();
            var geocoder = new GClientGeocoder();
            map.getInfoWindow();

            
            $('#toggle_map').toggle(function(){
                $('#map_canvas').css('z-index', -1);
                $('#map_canvas').removeClass('big-map-canvas');
                $(this).html("Show Map");
                $('div.restaurants-list').css('margin-top', '-40px');
            }, function(){
                $('#map_canvas').css('z-index', 0);
                $('#map_canvas').addClass('big-map-canvas');
                $(this).html("Hide Map");
                $('div.restaurants-list').css('margin-top', '5px');
            })
            if($('input[name=map_hidden]').val()=='true'){
                $('#toggle_map').trigger('click');
            }

                            
            $.getJSON(o.checkGeolocationURL, {}, function(data){
                if(data.geolocationEnabled){
                    o.userCurrAddress = data.userCurrAddress;
                    radiusCurrPos=userCurrLatLng = new GLatLng(data.userCurrLatLng.lat, data.userCurrLatLng.lng);
                    userCurrSearchRadius = o.defaultRadius = data.userCurrSearchRadius;
                    userHasAddress = true;
                    o.geolocationEnabled = true;                  
                }
                
                $.fn.prepareEnv(o.considerGL);
            });

            $.fn.prepareEnv = function(gl){
                o.considerGL = gl;
                geolocationEnabled = gl;
                map.setCenter(userCurrLatLng, o.defaultZoomLevel);
                if(o.considerGL){
                    o.geolocationEnabled = true;
                    prepareGLEnabledEnv();
                    if(!userHasAddress){
                        $.fn.myautolocate($.fn.geolocateSuccess, $.fn.notifyGeolocationUnsupported, o.permissionTimeout);//will be reloaded
                    }else{
                        $(o.currentLocHolder).text(o.userCurrAddress);
                        
                        updateUserLocation($.fn.reload);
                    }
                }else{
                    prepareGLDisabledEnv();
                    if(o.fetchResultsOnLoad){
                        if(userHasAddress){                            
                            $.fn.reload();
                        }else{ // best guess the user's address
                            guessUserAddress();
                        }
                    }                    
                }
            }
            function animateRestaurant(r){
                $(r).effect("highlight", {}, 2000);
            }

            function prepareGLDisabledEnv(){
                if(typeof(mapClickListener) != 'undefined')
                    GEvent.removeListener(mapClickListener);

                $.each(removableOverlays, function(){
                    map.removeOverlay(this);
                })
            }

            $.fn.geolocateSuccess = function(position){
                if(position instanceof GLatLng){
                    userCurrLatLng = position;
                }else{
                    userCurrLatLng = new GLatLng(position.coords.latitude, position.coords.longitude);
                }
                map.setCenter(userCurrLatLng, o.defaultZoomLevel);
                if(typeof(userCurrLatLng) != "undefined" || userCurrLatLng != null){
                    userLocMarker.setLatLng(userCurrLatLng);
                    updateUserLocation(function(){
                        $.fn.reload()
                    });
                //reload();
                }
            }

            function updateUserLocation(callback){
                geocoder.getLocations(userCurrLatLng, function(response){
                    place = response.Placemark[0];
                    o.userCurrAddress = place.address;
                    userHasAddress = true;
                    $(o.currentLocHolder).text(o.userCurrAddress);
                    callback.call();                    
                });
            }

            $.fn.reload = function(){
                map.clearOverlays();
                markers = [];                
                $.fn.fetchResults();
                if(o.considerGL){
                    
                    addUserLocMarker();
                    addRadiusMarker();
                    drawCircle();
                    
                }
            }

            function addUserLocMarker(){
                userLocIcon = new GIcon();
                userLocIcon.image = o.userLocIcon;
                userLocIcon.shadow = o.userLocIconShadow;
                userLocIcon.iconSize = new GSize(32, 32);
                userLocIcon.shadowSize = new GSize(59, 32);
                userLocIcon.iconAnchor = new GPoint(24, 20);
                userLocIcon.infoWindowAnchor = new GPoint(5, 1);
                locMarkerOptions = {
                    icon: userLocIcon,
                    draggable: true
                };
                userLocMarker = new GMarker(userCurrLatLng, locMarkerOptions);
                GEvent.addListener(userLocMarker, 'dragend', function(){                    
                    userCurrLatLng = userLocMarker.getLatLng();
                    map.panTo(userCurrLatLng);
                    positionRadiusMarker();
                    updateUserLocation(function(){
                        $.fn.reload()
                    });
                });
                removableOverlays.push(userLocMarker);
                map.addOverlay(userLocMarker);
            }

            function addRadiusMarker(){
                radiusIcon = new GIcon();
                radiusIcon.image = o.radiusIcon;
                radiusIcon.shadow = o.radiusIconShadow;
                radiusIcon.iconSize = new GSize(32, 32);
                radiusIcon.shadowSize = new GSize(59, 32);
                radiusIcon.iconAnchor = new GPoint(16, 16);
                radiusMarkerOptions = {
                    icon: radiusIcon,
                    draggable: true
                };

                //make sure radius marker is in correct position
                positionRadiusMarker();
                radiusMarker = new GMarker(radiusCurrPos, radiusMarkerOptions); //we will create the radius marker at the userLocMarker place
                GEvent.addListener(radiusMarker, 'dragend', function(){
                    userCurrSearchRadius = userLocMarker.getLatLng().distanceFrom(radiusMarker.getLatLng()) * 0.000621371192;
                    $('.ui-slider').slider("values", 0, userCurrSearchRadius*2);
                    //updateUserLocation(function(){
                    $.fn.reload();
                //});
                });
                removableOverlays.push(radiusMarker);
                map.addOverlay(radiusMarker);
            }

            function positionRadiusMarker(){
                latConv = userCurrLatLng.distanceFrom(new GLatLng(userCurrLatLng.lat()+0.1, userCurrLatLng.lng()))/100 * 0.621371192;
                lngConv = userCurrLatLng.distanceFrom(new GLatLng(userCurrLatLng.lat(), userCurrLatLng.lng()+0.1))/100 * 0.621371192;
                radiusCurrPos = new GLatLng(userCurrLatLng.lat()+(userCurrSearchRadius/latConv*Math.cos(Math.PI/180)),
                    userCurrLatLng.lng() + (userCurrSearchRadius/lngConv * Math.sin(Math.PI/180)));
            }

            /*
             * This is all math
             */
            function drawCircle(){
                lat = userLocMarker.getLatLng().lat();
                lng = userLocMarker.getLatLng().lng();
                d2r = Math.PI/180;
                r2d = 180/Math.PI;
                cLat = (userCurrSearchRadius/3963) * r2d;
                cLng = cLat/Math.cos(lat*d2r);

                cPoints = [];
                for(i = 0; i<=circleProp.noOfPoints; i++){
                    theta = Math.PI * (i/(circleProp.noOfPoints/2));
                    cX = lng + (cLng * Math.cos(theta));
                    cY = lat + (cLat * Math.sin(theta));
                    cPoints.push(new GPoint(cX, cY));
                }

                //                if(circle){
                //                    discoverMap.removeOverlay(circle);
                //                }

                circle = new GPolygon(cPoints, circleProp.sColor, circleProp.sWidth, circleProp.sOpacity, circleProp.fColor, circleProp.fOpacity);
                removableOverlays.push(circle);
                map.addOverlay(circle);
                $(o.displayRadiusHolder).html(Math.round(userCurrSearchRadius*100)/100);
            }

            function prepareGLEnabledEnv(){
                addUserLocMarker();
                addRadiusMarker();
                mapClickListener = GEvent.addListener(map, 'click', function(overlay, latLng){
                    if(latLng && overlay==null){
                        userCurrLatLng = latLng;
                        map.panTo(userCurrLatLng);
                        updateUserLocation(function(){
                            $.fn.reload()
                        });
                    //positionRadiusMarker();
                    //reload();
                    }
                });                
            }
            
            $.fn.fetchResults =  function (){
                resetLoadMore();
                defaultOptions = {
                    rawData: true,
                    userCurrLat: userCurrLatLng.lat(),
                    userCurrLng: userCurrLatLng.lng(),
                    userCurrAddress: o.userCurrAddress,
                    considerGL: o.considerGL,
                    userHasAddress: userHasAddress,
                    geolocationEnabled: o.geolocationEnabled,
                    userCurrSearchRadius: userCurrSearchRadius,
                    browserSupportsGL: browserSupportsGL,
                    loadMore: o.loadMore,
                    select: o.isSelect
                //sspn: typeof(circle)!="undefined"?circle.getBounds().toSpan().toUrlValue():"h"
                }

                additionalOptions = {};
                if(!o.loadMore){
                    allTags = new Array();
                    markerCounter=0;
                    if(o.isSearch){
                        query = $('form.search input.input').val();
                        if($.trim(query)!=''){
                            additionalOptions =  {
                                search:true,
                                q:query,
                                includeMenu: $('input[id=include_menu]').is(':checked'),
                                includeRestaurant: $('input[id=include_restaurant]').is(':checked'),
                                includeAddress: $('input[id=include_address]').is(':checked')
                            };
                        }else{
                            return;
                        }
                    }
                }

                
                //we will fetch raw data and update map accordingly we will
                //make another request to load formatted data
                $.getJSON(o.fetchResultsURL,
                    $.extend(defaultOptions, additionalOptions)
                    , function(result){
                        // various icons depending on how well its rated
                        green = new GIcon();
                        green.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/circular/greencirclemarker.png";
                        green.iconSize = new GSize(32, 32);
                        green.iconAnchor = new GPoint(16, 32);

                        yellow = new GIcon();
                        yellow.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/circular/yellowcirclemarker.png";
                        yellow.iconSize = new GSize(32, 32);
                        yellow.iconAnchor = new GPoint(16, 32);

                        red = new GIcon();
                        red.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/circular/bluecirclemarker.png";
                        red.iconSize = new GSize(32, 32);
                        red.iconAnchor = new GPoint(16, 32);
                        restaurants = result.restaurants;
                        

                        
                        
                        for (i = 0; i < restaurants.length; i++) {
                            rTags='';
                            $.each(restaurants[i].tags, function(){                                
                                $.each(this, function(k){
                                    k = $.trim(k);
                                    if($.inArray(k, allTags)==-1){
                                        allTags.push(k);
                                        rTags = rTags+' '+k;
                                    }
                                })
                            });
                            
                            var icon;
                            lat = restaurants[i].lat;
                            lng = restaurants[i].lng;

                            rating = restaurants[i].rating; // needs to be 1-5

                            // assign the appropriate colored icon based on the rating
                            if (rating > 3)
                                icon = green;
                            else if (rating > 1)
                                icon = yellow;
                            else
                                icon = red;
                            latlng = new GLatLng(lat, lng);
                            markerBounds.extend(latlng);
                            markerCounter++;
                            opts = {
                                "icon": icon,
                                "clickable": true,
                                "labelText": markerCounter, //text on marker
                                "labelOffset": new GSize(-5, -25)
                            }
                            if(typeof(radiusMarker)!="undefined")
                                markerBounds.extend(radiusMarker.getLatLng());
                            marker = new LabeledMarker(latlng, opts);
                            marker.restaurantName = restaurants[i].name; // name of the restaurant
                            marker.restaurantAddress = restaurants[i].address; // name of the item
                            marker.restaurantRating = restaurants[i].rating; // 0 - 5 stars
                            marker.restaurantLink =     restaurants[i].link;
                            marker.restaurantTags = rTags;
                            marker.slug = restaurants[i].slug;
                            
                            map.addOverlay(marker);
                            
                            markers.push(marker);
                            z = map.getBoundsZoomLevel(markerBounds)
                            //console.log(z);
                            zoom = z>14?20+(z-21):z;

                            map.setCenter(markerBounds.getCenter(),zoom);
                            
                            $.each(markers, function(){
                                GEvent.addListener(this, "click", function(){
                                    showTooltip(this);
                                    
                                });
                            })
                        }
                        

                        //load the formatted result
                        defaultOptions.rawData = false;

                        if(o.loadMore){
                            $.get(o.fetchResultsURL, $.extend(defaultOptions, additionalOptions), function(data){
                                $('#discover_results').append(data);
                                $.fn.setLoadMore(false);
                                
                                buildTags(allTags)
                                if(prepareLoadMore(restaurants.length) && o.isSearch) //returns true if there are more restaurants
                                    $.scrollTo('#'+restaurants[0].slug, 1000);
                            })
                        }else{
                            $('#discover_results').load(o.fetchResultsURL, $.extend(defaultOptions, additionalOptions)
                                , function(){
                                    $.fn.setLoadMore(false); //important
                                    //if(restaurants.length>0)
                                    buildTags(allTags);
                                    prepareLoadMore(restaurants.length);
                                    //lets load the google search results
                                    if(o.isSearch && o.isSelect=="true"&&query!='')
                                        $('#google_results').load(o.fetchGoogleResultsURL, {
                                            userCurrLat: userCurrLatLng.lat(),
                                            userCurrLng: userCurrLatLng.lng(),
                                            userCurrAddress: o.userCurrAddress,
                                            considerGL: o.considerGL,
                                            select: o.isSelect,
                                            q: query,
                                            sspn: typeof(circle)!="undefined"?circle.getBounds().toSpan().toUrlValue():""
                                        }, function(){
                                            //$('#content').append(data);
                                            //o.isSelect = false;
                                            });
                                     if(prepareLoadMore(restaurants.length)&&o.isSearch) //returns true if there are more restaurants
                                    $.scrollTo('#'+restaurants[0].slug, 1000);
                                })
                        }
                    });
            }

            function prepareLoadMore(l){
                $('p.load-more').html('LOAD MORE').removeClass('ui-state-active');
                $('#not_here_link').hide();
                if(l<1){
                    $('p.load-more').die('click').live('click', function(){
                        window.location = $(this).attr('rel');
                    }).html('That\'s all we have! Help us out and rate something you had recently. Submit a Tweat');
                    

                    $.scrollTo($('p.load-more'), 1000, {onAfter: function(){
                    $('p.load-more').show().effect("highlight", {color: '#cc0000'}, 1000);
                    }});
                    
                    
                    return false;

                }else if(l>0 && !($('p.load-more').is(':visible'))){
                    $('p.load-more').show();
                    $('#not_here_link').hide();
                    return true;
                }
                return true;
            }

            function buildTags(a){
                a.sort();
                //console.log(a);
                checkBoxesStr = '';
                checkBoxesStr = "<div id='tags_holder'><ul id='categories'>";
                

                $.each(a, function(key, val){
                    checkBoxesStr += '<li><input type="checkbox" name="category" + value="'+this+'" checked="true"><span>'+this+'</span></li>';
                })


                checkBoxesStr += "</ul></div>";
                $(o.tagsHolder).replaceWith(checkBoxesStr);
                $(o.tagsHolder+' ul').vchecks();

            }

            function showTooltip(marker)
            {
                // below: create the html to display in the tooltip
                tooltip.innerHTML = "<div>";
                tooltip.innerHTML += "<h3><a href='"+marker.restaurantLink+"'>"+marker.restaurantName + "</a></h3>";
                tooltip.innerHTML += "<h4>"+marker.restaurantAddress + "</h4>";
                tooltip.innerHTML += "<strong>Tags:</strong> " + marker.restaurantTags + "<br />";
                tooltip.innerHTML += "<span style=\"text-align: center;\"><strong>Rating:</strong> ";
                // loop through 5 times to creat our star rating system
                for (var i = 0; i < 5; i++)
                {
                    if (marker.restaurantRating <= i)
                        tooltip.innerHTML += "<img src=\"/images/whitestar.png\" />";
                    else
                        tooltip.innerHTML += "<img src=\"/images/yellowstar.png\" />";
                }
                tooltip.innerHTML += "</span>";
                tooltip.innerHTML += "<h6><a href='#"+marker.slug+"'> Show Below"+"</a></h6>";
                tooltip.innerHTML += "</div>";
                tooltip.style.visibility = "visible"; // show the tooltip
                map.openInfoWindowTabsHtml(marker.getLatLng(), tooltip);
            }

            $.fn.notifyGeolocationUnsupported = function(){
                o.geolocationEnabled = false;
                //we will show a modal box and guess the position                
                $(o.glNotSupportedNotification).dialog('open');
                
            //https://developer.mozilla.org/En/Using_geolocation
            }

            function guessUserAddress(){
                $.getJSON(o.guessUserAddressURL, function(data){
                    userCurrLatLng = new GLatLng(data['Latitude'], data['Longitude']);
                    map.setCenter(userCurrLatLng, o.defaultZoomLevel);
                    updateUserLocation($.fn.reload);                
                });
            }

            $.fn.geolocateByAddress = function(address, successCallback){
                geocoder.getLatLng(address, function(latLng){
                    if(!latLng){
                        console.log('@todo[ show error somewhere ]: '+ address + ' not found');
                    }else{
                        successCallback(latLng);
                    }
                })
            }

            $(o.addressInputBtn).click(function(){
                address = $(o.addressInput).val();
                if(address.length>0){
                    $.fn.geolocateByAddress(address, $.fn.geolocateSuccess);
               
                }
            });


            $('select'+o.radiusSelectSlider).val(o.defaultRadius);
            radiusSlider = $('select').selectToUISlider({
                labels: 6,
                sliderOptions:{
                    stop: function(event, ui){
                        userCurrSearchRadius = ui.value/2;
                        //updateUserLocation(function(){
                        //$.fn.resetMarkerBounds();
                        $.fn.reload();
                        
                    //});
                    }
                }
            }).hide(); //hides the select box

            $.fn.setLoadMore = function(b){
                o.loadMore = b;
            }

            $.fn.triggerMarker = function(i){
                showTooltip(markers[i-1]);
                document.getElementById('map_canvas').scrollIntoView(true);
                
            }

            $.fn.resetMarkerBounds = function(){
                markerBounds = new GLatLngBounds();
            }
            function resetLoadMore(){
                if(!o.loadMore){
                    $('p.load-more').die('click').live('click', function(){
                        $('p.load-more').html('LOADING...').addClass('ui-state-active');
                        $.fn.setLoadMore(true);
                        $.fn.fetchResults();

                    });
                }
            }
        }//end of this plugin
    });
})(jQuery);