var tooltip;

(function($, tooltip){
    /* jquery tooltip*/

$.fn.tooltip = function(html) {
    var obj = $(this);
    var txt = (html)?html:obj.attr('title');
    obj.attr('title','');
    if (!tooltip) tooltip = $('<p id="tooltip"></p>').appendTo($('body')).css({
        'position':'absolute','background':'#fff','z-index':'999999',
        'border':'1px solid #555', 'padding':'4px'}).hide();

    obj.css('cursor','pointer').hover(function(e){
            tooltip.html(txt).css("top",(e.pageY - 10) + "px").css("left",(e.pageX + 20) + "px").fadeIn('fast');
            },
            function(){
                tooltip.hide();
            }).mousemove(function(e){
            tooltip.css("top",(e.pageY - 10) + "px").css("left",(e.pageX + 20) + "px");
        });
    return obj;
    }
})(jQuery, tooltip);

var sensimap = {
    groups_hooked: false,
    initialized: false,
    debug: false,
    map: null,
    listedads: [],
    icons:{},
    sync_field:null,
    text_field:null,
    geocoder:null,
    geocoding:false,
    displayProjection:new OpenLayers.Projection("EPSG:4326")
};

OpenLayers.Util.onImageLoadErrorColor = '#fff';


sensimap.build_agr_icons = function(icon_data) {
    sensimap.agr_cnt = icon_data.agr.length;
    var agr = new Array(sensimap.agr_cnt);

    for (var i=0; i<sensimap.agr_cnt; i++) {
        var data = icon_data.agr[i];
        var size = new OpenLayers.Size(data.size[0], data.size[1]),
            //offset = new OpenLayers.Pixel(-data.size[0]/2, -data.size[1]/2);
            offset = new OpenLayers.Pixel(-data.size[0]/2, -data.size[1]);

        agr[i] = {
            'image': "url('"+ icon_data.sprite +"')",
            'position': '-'+data.position[0]+'px -'+data.position[1]+'px',
            'position_hover': '-'+(data.position[0]+65)+'px -'+data.position[1]+'px',
            'size': size,
            'class_name': 'agr'+(i+1),
            'icon': new OpenLayers.Icon(icon_data.pixel, size, offset)
            }
        }
    sensimap.icons['agr'] = agr;
   }

sensimap.build_obj_icon = function(icon_data) {
    var data = icon_data.obj;
    var size = new OpenLayers.Size(data.size[0], data.size[1]),
        offset = new OpenLayers.Pixel(-data.size[0]/2, -data.size[1]);
    sensimap.icons.obj = {
        'image': "url('"+ icon_data.sprite +"')",
        'position': '-'+data.position[0]+'px -'+data.position[1]+'px',
        'position_hover': '-'+(data.position[0]+size.w)+'px -'+data.position[1]+'px',
        'size': size,
        'icon': new OpenLayers.Icon(icon_data.pixel, size, offset)
        }
   }

sensimap.build_nb_icons = function(icon_data) {
    var data = icon_data.nb_l;
    var size = new OpenLayers.Size(data.size[0], data.size[1]),
        offset = new OpenLayers.Pixel(-data.size[0]/2, -data.size[1]);
    var icon = new OpenLayers.Icon(icon_data.pixel, size, offset)

    var nb = new Array(icon_data.nb_cnt);
    for (var i=0; i<icon_data.nb_cnt; i++) {
        nb[i] = {
            'image': "url('"+ icon_data.sprite +"')",
            'position': '-'+data.position[0]+'px -'+(data.position[1]+(i*size.h))+'px',
            'position_hover': '-'+(data.position[0]+size.w)+'px -'+(data.position[1]+size.h)+'px',
            'size': size,
            'icon': icon
            }
        }
    sensimap.icons.nb = nb;
   }


sensimap.group_redirect = function(g) {
    var bounds = false;
    if(g) {
        bounds = g.bounds;
    } else {
        bounds = sensimap.map.getExtent();
    }
    if (bounds) window.location = sensimap.options.group_redirect+encodeURIComponent(bounds.transform(sensimap.map.getProjectionObject(),sensimap.displayProjection).toBBOX().toString());
}

sensimap.get_groups = function(bounds, extra_params, extra) {
    if (((sensimap.options.group &&(sensimap.map.getZoom()>sensimap.options.group_minzoom))||sensimap.hidden)||(!sensimap.options.group)) {
        return ((extra) ? extra() : true);
    }
    if (!bounds) {
        bounds = sensimap.map.getExtent().transform(sensimap.map.getProjectionObject(),sensimap.displayProjection);
    }

    var url = sensimap.options.group + '?m=' + encodeURIComponent(bounds.toBBOX().toString());
   
    if (extra_params !== undefined) {
        url += '&' + extra_params;
    } 

    $.getJSON(url, function(data) {
        sensimap.groups.addJSON(data, extra);
    });
}

sensimap.connect_objs = function(target) {
    if (!target) {
        target = sensimap.options.connect_obj;
    }
    var hookgroups = (sensimap.options.group &&(sensimap.map.getZoom()<=sensimap.options.group_minzoom));
    target.find(sensimap.options.connect_obj_selector).each(function(cnt) {
        var row = $(this);
        var counter = row.find('.ad_counter').text()
        var lonlat = row.find(sensimap.options.connect_coords_selector).text();
        if (lonlat && lonlat !== '{}') {
            lonlat = JSON.parse(lonlat);
            if (hookgroups) {
                if (sensimap.groups.hook(lonlat['lng'], lonlat['lat'], row)) {
                    sensimap.groups_hooked = true;
                }
            } else {
                sensimap.addAd(lonlat['lng'], lonlat['lat'], row, counter, cnt+1);
            }
        }
    });

    if (sensimap.options.connect_obj_zoom) {
        var bounds = new OpenLayers.Bounds();
        if (sensimap.ad_markers.markers.length) {
            for (var i=0; i<sensimap.listedads.length; i++) {
                bounds.extend(sensimap.listedads[i].lonlat)
            }
            bounds = bounds.scale(1.2);
            sensimap.map.zoomToExtent(bounds)
        } else {
            sensimap.go_to(sensimap.options.initial_location);
        }
    }
}

sensimap.init = function(opts) {
    sensimap.defaults = {
        transparency: 0.45,
        icon_path: '/',
        nb_popup_size: [300, 70],
        ad_popup_size: [300, 140],
        html_group_tooltip: "${count}",
        ad_popup_html: "${title}",
        get_ad_data:function(a) {
            return {'title':a.find('.advertTitle').html()}
        },
        post_move: function(bounds) {;},
        zoomoffset:3,
        group:false,
        group_minsize: 2,
        group_minzoom: 5,
        group_clustergridsize: 6, // serverside grid density
        group_clustersizediff: 10, // w
        group_redirect: false,
        connect_obj: false,
        connect_obj_selector: '.ad',
        connect_coords_selector: '.google_map',
        connect_obj_zoom: false,
        connect_obj_popups: true,
        sync_field: $('.vMapSearchCustomField'),
        text_field: $('input#near_to'),
        text_field_live: false,


        sprite:'/',
        obj:{'position':[100,100], 'size':[100,100], 'shadow':{'position':[100,100], 'size':[100,100]}},
        obj_l:{'position':[100,100], 'size':[100,100], 'shadow':{'position':[100,100], 'size':[100,100]}},
        obj_cnt:10,
        agr:[{'position':[960,615], 'size':[63,63]},
                {'position':[1025,750], 'size':[57,57]},
                {'position':[960,880], 'size':[37,37]}
              ],
        nb_l:{'position':[100,100], 'size':[100,100], 'shadow':{'position':[100,100], 'size':[100,100]}},
        nb_cnt: 10,
        hidden:false,
        use_labeled_markers: false
    }
    sensimap.popup = null; // one popup open on map at once.

    sensimap.options = $.extend(sensimap.defaults, opts);
    // temporary fix allowing aditional overrides. coupling with search needs to be simplified so it can be used
    // in js directly /and/or/ a key word based template tag for easy overriding
    sensimap.options = $.extend(sensimap.options, (window.map_options_overrides||{}));
    sensimap.hidden = sensimap.options.hidden;
    OpenLayers.ImgPath = sensimap.options.icon_path;
    sensimap.options = $.extend(sensimap.options, (window.icon_data||{}));
    sensimap.options.ad_popup_size = new OpenLayers.Size(sensimap.options.ad_popup_size[0], sensimap.options.ad_popup_size[1]);
    sensimap.options.nb_popup_size = new OpenLayers.Size(sensimap.options.nb_popup_size[0], sensimap.options.nb_popup_size[1]);

    sensimap.html_ad_popup = sensimap.options.ad_popup_html;

    sensimap.html_group_tooltip = sensimap.options.html_group_tooltip;

    sensimap.adData = (sensimap.options.get_ad_data) ? sensimap.options.get_ad_data : (function(a) {
        return {
            'title': a.find('.title strong').html(),
            'price': a.find('.value.price').text()
        };
    });

    // @deprecated
    if (!sensimap.hidden) {
        sensimap.init_internal();
    }
};


sensimap.init_internal = function() {
    var map = new OpenLayers.Map('sensimap', {
        projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
        units: "m",
        maxResolution: 156543.0339,
        theme:'',
        controls: [
            new OpenLayers.Control.Navigation({zoomWheelEnabled:false})
        ]
    });
    sensimap.map = map;
    /* build icons */
    sensimap.nb = $("#neighbourhood");
    sensimap.nb_checkboxes = sensimap.nb.find('input');
    sensimap.nb_sources = sensimap.nb.find('#sources');

    sensimap.build_agr_icons(sensimap.options);
    sensimap.build_obj_icon(sensimap.options);

    var nb_items = [];
    sensimap.nb.find('.checkbox').each(function() {
        var span = $(this);
        var keyword = $(this).attr('id');
        var chbx = span.find('input');
        nb_items.push( {
            checkbox: chbx,
            keyword: keyword});
        });

    if (nb_items.length) {
        sensimap.build_nb_icons(sensimap.options);
        sensimap.init_neighbourhood(nb_items);
    }

    var gops = {
        sphericalMercator: true,
        numZoomLevels: 16,
        maxZoomLevel: 19,
        minZoomLevel: 3,
        maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34)
    };

    var gmap = new OpenLayers.Layer.Google("Google Streets", gops, {transitionEffect: 'resize'});
    gops['type'] = G_HYBRID_MAP;
    var ghyb = new OpenLayers.Layer.Google("Google Hybrid", gops);
    gops['type'] = G_SATELLITE_MAP;
    var gsat = new OpenLayers.Layer.Google("Google Satellite", gops);

    sensimap.map.addLayers([gmap, ghyb, gsat]);

    /* layer controls */
    var switchers = $('<div id="layerswitcher"> </div>').css({'z-index':'1020', 'position':'absolute', 'right':'10px'}).appendTo($('#sensimap_OpenLayers_ViewPort'));

    $('<div class="streets">Streets</div>').click(function() {
        sensimap.map.setBaseLayer(gmap);
    }).appendTo(switchers);

    $('<div class="hybrid">Hybrid</div>').click(function() {
        sensimap.map.setBaseLayer(ghyb);
    }).appendTo(switchers);

    $('<div class="sattelite">Sattelite</div>').click(function() {
        sensimap.map.setBaseLayer(gsat);
    }).appendTo(switchers);

    sensimap.testms = new OpenLayers.Layer.Boxes("test_markers");
    sensimap.map.addLayer(sensimap.testms);

    sensimap.groups = new sensimap.GroupMarkers();
    sensimap.ad_markers = new OpenLayers.Layer.Markers("ad_markers");
    sensimap.map.addLayer(sensimap.ad_markers);

    var panzoom = new OpenLayers.Control.PanZoomBar({
        'div': OpenLayers.Util.getElement('paneldiv')
    });
    document.test = panzoom;
    map.addControl(panzoom);

    /* fixme: tooltip properties options */
    if (sensimap.options.default_location && !sensimap.options.initial_location) {
        sensimap.options.initial_location = sensimap.options.default_location;
    }
    if (sensimap.options.initial_location) {
        if (sensimap.options.initial_location.top) {
            var b = sensimap.options.initial_location;
            sensimap.go_to(sensimap.options.initial_location);
        } else {
            var addMarker = function(lat, lng) {
                var marker = sensimap.addAd(lng, lat);
                sensimap.map.setCenter(marker.lonlat.clone(), (sensimap.options.initial_location.zoom||16) - sensimap.options.zoomoffset);
            };
            if(sensimap.options.initial_location.city) {
                sensimap.go_to_address(sensimap.options.initial_location, addMarker);
            } else {
                addMarker(sensimap.options.initial_location.lat, sensimap.options.initial_location.lng);
            }
        }
    }

    sensimap.map.events.register('click', sensimap.map, function (e) {
        if (sensimap.popup) {
            sensimap.popup.hide();
        }
    });

    if (sensimap.options.group_redirect) {
        sensimap.map.events.register('zoomend', sensimap.map, function (e) {
        if (sensimap.map.getZoom()>sensimap.options.group_minzoom) {
            sensimap.group_redirect(false);
            OpenLayers.Event.stop(e);
            }
        });
    }

    sensimap.map.events.register('moveend', sensimap.map, function (e) {
        var bounds = sensimap.sync();
        sensimap.options.post_move(bounds);
    });
    sensimap.initialized = true;
    if (sensimap.options.connect_obj) {
        sensimap.connect_objs(null);
    };

    var with_search = false;
    // bcfix - remove when searcher will be gone
    if (window.SENSI_SETTINGS.use_new_searcher) {
        if('searcher_next_gen' in $){
            with_search = $.searcher_next_gen.is_usable();
        }
    } else {
        if('searcher' in $){
            with_search = $.searcher.is_usable();
        }
    }
    // we want to control if we use searcher or not through options
    if (typeof(sensimap.options.with_search) !== 'undefined') {
        with_search = sensimap.options.with_search;
    }

    sensimap.options.text_field_live = with_search;
    if (sensimap.options.group) {
        sensimap.set_sync_field(sensimap.options.sync_field);
        sensimap.set_text_field(sensimap.options.text_field, sensimap.options.text_field_live);
    }
    
    if (with_search) {
        sensimap.update = function(results, clusters) {
            if (sensimap.hidden) return;
            sensimap.clearlisted();
            sensimap.groups.clearGroups();
            sensimap.groups_hooked = false;

            // bcfix - remove when searcher will be gone
            if (window.SENSI_SETTINGS.use_new_searcher) {
                sensimap.get_groups(false, $.searcher_next_gen.get_params(), function() {
                    sensimap.connect_objs(sensimap.options.connect_obj);
                });
            } else {
                sensimap.get_groups(false, $.searcher.get_params(), function() {
                    sensimap.connect_objs(sensimap.options.connect_obj);
                });
            }
        };

        sensimap.options.post_move = function(bounds) {
            sensimap.clearlisted();
            sensimap.groups.clearGroups();

            // bcfix - remove when searcher will be gone
            if (window.SENSI_SETTINGS.use_new_searcher) {
                $.searcher_next_gen.run()
            } else {
                $.searcher.run()
            }
        };

        // bcfix - remove when searcher will be gone
        if (window.SENSI_SETTINGS.use_new_searcher) {
            sensimap.options.connect_obj = $.searcher_next_gen.addcallbacks('sensimap', {
                after: sensimap.update
            });
        } else {
            sensimap.options.connect_obj = $.searcher.addcallbacks('sensimap', {
                after: sensimap.update
            });
        }

        if (!sensimap.options.hidden) {
            sensimap.update();
        }
    } else if (sensimap.options.group) {
        sensimap.options.post_move = function(bounds) {
            sensimap.groups.clearGroups();
            sensimap.get_groups(bounds);
        };

        sensimap.get_groups();
    }

};

sensimap.update = function() {
};

sensimap.hide = function() {
    sensimap.hidden = true;
};

sensimap.show = function() {
    if (!sensimap.initialized) {
        sensimap.init_internal();
        sensimap.sync_field && sensimap.sync()
    }
    sensimap.hidden = false;
    sensimap.update();
}

sensimap.go_to = function(b) {
    if (!b) {
        b = sensimap.options.default_location;
    }
    sensimap.map.zoomToExtent(new OpenLayers.Bounds(b.left, b.bottom, b.right, b.top).transform(sensimap.displayProjection, sensimap.map.getProjectionObject()));
}

sensimap.go_to_address = function(addr, cb) {
    var geocoder = new GClientGeocoder();
    if (typeof(addr.hints)!='undefined' && addr.hints.length > 0) {
        location_str = addr.hints;
    }
    else {
        location_str = addr.address+','+addr.zip+','+addr.city+','+addr.state;
    }
    geocoder.getLocations(location_str, function(response) {
        if (response && response.Status.code == 200) {
            if (response.Placemark.length) {
                var p = response.Placemark[0];
                sensimap.map.zoomToExtent(new OpenLayers.Bounds(
                    p.ExtendedData.LatLonBox.west,
                    p.ExtendedData.LatLonBox.south,
                    p.ExtendedData.LatLonBox.east,
                    p.ExtendedData.LatLonBox.north
                    ).transform(sensimap.displayProjection, sensimap.map.getProjectionObject()));
                cb(p.Point.coordinates[1], p.Point.coordinates[0]);
            }
        }
    });
};

sensimap.get_initial = function() {
    alert(sensimap.map.getExtent().transform(sensimap.map.getProjectionObject(),sensimap.displayProjection).toBBOX().toString());
};

sensimap.sync = function() {
    var bounds = sensimap.map.getExtent().transform(sensimap.map.getProjectionObject(),sensimap.displayProjection);
    if (sensimap.sync_field) {
        sensimap.sync_field.val(bounds.toBBOX().toString());
        if (sensimap.geocoding) {
            sensimap.geocoding = false;
        } else if (sensimap.text_field) sensimap.text_field.val('');
    }
    return bounds;
};

sensimap.set_sync_field = function(f) {
    sensimap.sync_field = f; sensimap.initialized && sensimap.sync()
};

sensimap.set_text_field = function(f) {
    var live = sensimap.options.text_field_live;
    if (!live && sensimap.sync_field) sensimap.sync_field.val('');
    sensimap.text_field = f.change(function() {
        if (live) {
            var query = $(this).val().trim();
            if (query) {
                if (!sensimap.geocoder) sensimap.geocoder = new GClientGeocoder();
                sensimap.geocoding = true; // halt manual search until it's done.
                sensimap.geocoder.getLocations(query, function(response) {
                    if (response && response.Status.code == 200) {
                        //done();
                        if (response.Placemark.length) {
                            var p = response.Placemark[0];
                            //sensimap.geocoding = false; - in sync
                            sensimap.map.zoomToExtent(new OpenLayers.Bounds(
                                p.ExtendedData.LatLonBox.west,
                                p.ExtendedData.LatLonBox.south,
                                p.ExtendedData.LatLonBox.east,
                                p.ExtendedData.LatLonBox.north
                                ).transform(sensimap.displayProjection, sensimap.map.getProjectionObject()));

                            //sensimap.text_field.val('') - in sync
                        } else {
                            sensimap.geocoding = false;
                            sensimap.text_field.val('')
                            //done();
                           ;
                        }
                    } else {
                        sensimap.geocoding = false;
                        sensimap.text_field.val('')
                       //done();
                       ;
                    }
                });
            }
      } else {
        if (sensimap.sync_field) sensimap.sync_field.val('');
      }
    });
};

sensimap.nb_should_activate = function() {
    return (sensimap.map.getZoom()>10);
};

sensimap.nb_disable = function() {
    sensimap.nb.addClass('disabled');
    sensimap.nb_checkboxes.attr('disabled', 'disabled');
    $.each(sensimap.nb_layers, function() { this.setVisibility(false); });
    sensimap.nb_active = false;
    sensimap.nb_sources.hide();
};

sensimap.nb_enable = function() {
    sensimap.nb.removeClass('disabled');
    sensimap.nb_checkboxes.removeAttr('disabled');
    $.each(sensimap.nb_layers, function() {
        if (this.active) {
            this.setVisibility(true);
        }
    });
    sensimap.nb_active = true;
    sensimap.nb_sources.show();
};

sensimap.init_neighbourhood = function(nb) {
    sensimap.nb_layers = {};
    sensimap.nb_active = sensimap.nb_should_activate()
    if (!sensimap.nb_active) sensimap.nb_disable();

    for (i in nb) {
        var nb_type = nb[i];
        var nblayer = new sensimap.nbLayer(nb_type.keyword, nb_type.checkbox);
        sensimap.nb_layers[nb_type.keyword]=nblayer;
        sensimap.map.addLayer(nblayer);
    }

    sensimap.map.events.register('moveend', sensimap.map, function (e) {
        var c = sensimap.map.getCenter().clone().transform(sensimap.map.getProjectionObject(),sensimap.displayProjection);
        var b = sensimap.map.getExtent().transform(sensimap.map.getProjectionObject(),sensimap.displayProjection);
        if (sensimap.nb_should_activate()) {
            $.each(sensimap.nb_layers, function() {
                this.set_search_params({x:c.lon, y:c.lat}, {"sspn": b.top - b.bottom + ", " + b.right - b.left});
                this.run_search();
            });
            if (!sensimap.nb_active) {
                sensimap.nb_enable();
            }
        } else if (sensimap.nb_active) {
            sensimap.nb_disable();
        }
    });
};

sensimap.remove_popup = function() {
    if (!sensimap.popup) {
        return;
    }
    sensimap.map.removePopup(sensimap.popup);
    sensimap.popup.hide();
    sensimap.popup.destroy();
    sensimap.popup = null;
};

sensimap.nbPopup = OpenLayers.Class(OpenLayers.Popup, {
    'panMapIfOutOfView':true,

    initialize: function(marker, result) {
        html = '<p><a href="'+result.url+'">'+result.title+'</a></p>';
        if (result.phoneNumbers) {
            html+='<p>'+result.phoneNumbers[0].number+'</p>';
        }

        OpenLayers.Popup.prototype.initialize.call(this, "nbpopup", marker.lonlat.clone(),
            sensimap.options.nb_popup_size.clone(), html, false);

        sensimap.map.addPopup(this);
        this.marker = marker;
    }
});

sensimap.adPopup = OpenLayers.Class(OpenLayers.Popup.Anchored, {

    initialize: function(marker, ad_listing) {
        OpenLayers.Popup.Anchored.prototype.initialize.call(this, "adpopup", marker.lonlat.clone(), sensimap.options.ad_popup_size.clone(), '', marker.icon, false);
        this.setContentHTML(OpenLayers.String.format(sensimap.html_ad_popup, sensimap.adData(ad_listing)));
        sensimap.map.addPopup(this);
        this.marker = marker;
        this.listing = ad_listing;
        this.listing.addClass('selected');
    },

    hide: function() {
        this.listing.removeClass('selected');
        OpenLayers.Popup.prototype.hide.call(this);
    },

    show: function() {
        this.listing.addClass('selected');
        OpenLayers.Popup.prototype.show.call(this);
    }

});

sensimap.popup_onclick = function(popupclass, marker, obj){
    return function(evt) {
        if ((sensimap.popup!=null)&&(sensimap.popup.marker==marker)) {
            sensimap.popup.toggle();
        } else {
            if (sensimap.popup) { sensimap.remove_popup(); };
            sensimap.popup = new popupclass(marker, obj);
        }
        OpenLayers.Event.stop(evt);
    };
};


sensimap.spriteMarker = OpenLayers.Class.create();
sensimap.spriteMarker.prototype = OpenLayers.Class.inherit(OpenLayers.Marker, {
    enabled: true,
    initialize: function(pos, data, counter) {
        OpenLayers.Marker.prototype.initialize.call(this, pos, data.icon.clone());
        this.div = this.icon.imageDiv;
        this.pos = data.position;
        this.pos_h = data.position_hover;
        this.div.style.backgroundPosition = this.pos;
        this.div.style.backgroundImage = data.image;
        this.div.style.cursor = 'pointer';
        if (counter) {
            if (!data.class_name) {data.class_name = 'pin'; };
            this.div.className = data.class_name;
            var cnt = document.createElement("div");
            cnt.className = 'counter';
            cnt.innerHTML= counter;
            this.div.appendChild(cnt);
        }
    }});

// http://n2.nabble.com/marker-label-td1831504.html #17121
sensimap.labeledMarker = OpenLayers.Class.create();
sensimap.labeledMarker.prototype = OpenLayers.Class.inherit(sensimap.spriteMarker, {
    initialize: function(pos, data, counter, label) {
        sensimap.spriteMarker.prototype.initialize.call(this, pos, data, counter);
        this.label = label;
        this.markerDiv = OpenLayers.Util.createDiv();
        this.markerDiv.appendChild(this.icon.imageDiv);
        var txtDiv = OpenLayers.Util.createDiv();
        txtDiv.className = 'markerLabel';
        OpenLayers.Util.modifyDOMElement(txtDiv, null, new OpenLayers.Pixel(10, 10));
        txtDiv.innerHTML = this.label;
        this.markerDiv.appendChild(txtDiv);
    },
    /**
     * Method: destroy
     * Nullify references and remove event listeners to prevent circular
     * references and memory leaks
     */
    destroy: function() {
        sensimap.spriteMarker.prototype.destroy.apply(this, arguments);
        this.markerDiv.innerHTML = "";
        this.markerDiv = null;
    },

    draw: function(px) {
        OpenLayers.Util.modifyAlphaImageDiv(this.icon.imageDiv,
            null,
            null,
            this.icon.size,
            this.icon.url);
        OpenLayers.Util.modifyDOMElement(this.markerDiv, null, px.offset(this.icon.offset));
        return this.markerDiv;
    },

    redraw: function(px) {
        if ((px != null) && (this.markerDiv != null)) {
            OpenLayers.Util.modifyDOMElement(this.markerDiv, null, px.offset(this.icon.offset));
        }
    },

    moveTo: function(px) {
        this.redraw(px);
        this.lonlat = this.map.getLonLatFromLayerPx(px);
    },

    isDrawn: function() {
        return false;
    },

    CLASS_NAME: "sensimap.labeledMarker"
});

/* marker with handlers highlighting corresponding ads on listings */
sensimap.adMarker = OpenLayers.Class.create();
sensimap.adMarker.prototype = OpenLayers.Class.inherit(sensimap.spriteMarker, {
    enabled: true,
    initialize: function(pos, listing_row, counter) {
        var data = sensimap.icons.obj;
        sensimap.spriteMarker.prototype.initialize.call(this, pos, data, counter);
        this.listing = listing_row;
        if (listing_row && sensimap.options.connect_obj_popups) {
        this.events.register("click", this, sensimap.popup_onclick(sensimap.adPopup, this, listing_row));
        listing_row.click(function(marker){return function(){marker.events.triggerEvent('click')}}(this));
        }
    },
    on: function(e) {
        this.listing.addClass('on');
        this.div.style.backgroundPosition = this.pos_h;
        if (e) OpenLayers.Event.stop(e);
        },

    off: function(e) {
        this.listing.removeClass('on');
        this.div.style.backgroundPosition = this.pos;
        if (e) OpenLayers.Event.stop(e);
        }
});

sensimap.labeledAdMarker = OpenLayers.Class.create();
sensimap.labeledAdMarker.prototype = OpenLayers.Class.inherit(sensimap.labeledMarker, {

    enabled: true,

    initialize: function(pos, listing_row, counter, label) {
        var data = sensimap.icons.obj;
        sensimap.labeledMarker.prototype.initialize.call(this, pos, data, counter, label);
        this.listing = listing_row;
        $('.labelOnList', listing_row).html('<span class="markerHolder">' + label + '</span>');
        if (listing_row && sensimap.options.connect_obj_popups) {
            this.events.register("click", this, sensimap.popup_onclick(sensimap.adPopup, this, listing_row));
            listing_row.click(function(marker){return function(){marker.events.triggerEvent('click')}}(this));
        }
    },

    on: function(e) {
        this.listing.addClass('on');
        this.div.style.backgroundPosition = this.pos_h;
        if (e) OpenLayers.Event.stop(e);
    },

    off: function(e) {
        this.listing.removeClass('on');
        this.div.style.backgroundPosition = this.pos;
        if (e) OpenLayers.Event.stop(e);
    }

});

sensimap.clearlisted = function() {
    sensimap.remove_popup();

    for (i in sensimap.listedads) {
        sensimap.ad_markers.removeMarker(sensimap.listedads[i]);
        sensimap.listedads[i].destroy();
    }

    sensimap.listedads = [];
};

sensimap.addAd = function(lon, lat, ad_on_listing, counter, label) {
    var pos = new OpenLayers.LonLat(lon,lat).transform(sensimap.displayProjection, sensimap.map.getProjectionObject());
    var marker = null;
    if (sensimap.options.use_labeled_markers && label && !counter) {
        marker = new sensimap.labeledAdMarker(pos, ad_on_listing, counter, label);
    } else {
        marker = new sensimap.adMarker(pos, ad_on_listing, counter);
    }

    if (ad_on_listing) {
        marker.events.register("mouseover", marker, function() {
            marker.on();
            $('.listItem', ad_on_listing).addClass('hover');
        });
        marker.events.register("mouseout", marker, function() {
            marker.off();
            $('.listItem', ad_on_listing).removeClass('hover');
        });
        ad_on_listing.hover(function() {
            marker.on();
        }, function() {
            marker.off();
        });
    }

    sensimap.ad_markers.addMarker(marker);
    sensimap.listedads.push(marker);
    return marker;
};


/* neighbourhood layer with a localsearch object */
sensimap.nbLayer = OpenLayers.Class(OpenLayers.Layer.Markers, {
    initialize: function(name, checkbox) {
        var nb_key_int = name.split("_");
        OpenLayers.Layer.Markers.prototype.initialize.call(this, name);
        this.searched = false;
        this.icon_data = sensimap.icons.nb[parseInt(nb_key_int[2])-1];
        this.keyword = nb_key_int[1];
        this.res_cache = {};
        this.active = checkbox.attr('checked');
        this.localsearch = new google.search.LocalSearch();
        this.localsearch.setAddressLookupMode( google.search.LocalSearch.ADDRESS_LOOKUP_DISABLE );
        this.localsearch.setSearchCompleteCallback(this, sensimap.nbLayer.prototype.add_results, [this.localsearch]);
        var self = this;
        checkbox.click(function(){
            if (checkbox.attr('checked')) {
                self.show();
            } else {
                self.hide();
            }
        });
    },

    run_search: function() {
        if (this.active) {
            this.searched = true;
            this.localsearch.execute(this.keyword);
        }
    },

    set_search_params: function(center, span) {
        /* Set the search params after a map move. Only search if the layer is activated.
           if the layer was activated without a map move we only search if neccesary */
        this.searched = false;
        this.localsearch.setCenterPoint(center);
        this.localsearch.setRestriction(google.search.Search.RESTRICT_EXTENDED_ARGS, span);
    },

    in_cache: function(xy) {
        /* don't render results we already got. return true if we already have a marker in this location */
        var seen = this.res_cache[xy];
        if (!seen) this.res_cache[xy] = true;
        return seen;

    },

    add_results: function(searcher) {
        for (i in searcher.results) {
            var r = searcher.results[i];
            if (!this.in_cache(r.lng+"-"+r.lat)) {
                var pos = new OpenLayers.LonLat(r.lng,r.lat).transform(sensimap.displayProjection, sensimap.map.getProjectionObject());
                var marker = new sensimap.spriteMarker(pos, this.icon_data)
                this.addMarker(marker);
                marker.events.register("click", marker, sensimap.popup_onclick(sensimap.nbPopup, marker, r));
            }
        }
        sensimap.nb_sources.empty()
        var attr = searcher.getAttribution();
        if (attr) sensimap.nb_sources.append(attr);
    },

    hide: function() {
        this.active = false;
        this.setVisibility(false);
    },

    show: function() {
        this.active = true;
        if (!this.searched) {
            this.run_search();
        }
        this.setVisibility(true);
    },

    CLASS_NAME: "sensimap.nbLayer"
});


sensimap.GroupMarker = OpenLayers.Class.create();
sensimap.GroupMarker.prototype = OpenLayers.Class.inherit(sensimap.spriteMarker, {
    initialize: function(g) {
        /* map 6 digits to biggest agregator, and then smaller ones for smaller numbers */
        var data = sensimap.icons.agr;
        data = data[ Math.min(data.length-1,Math.max(Math.floor((6-g.count.toString().length)/2), 0)) ];
        data.size = new OpenLayers.Size(5,5);
        this.bounds = g.bounds;
        //g.bounds = g.margin;
        sensimap.spriteMarker.prototype.initialize.call(this, g.center.clone(), data, g.count);


        this.events.register("click", this, function(evt) {
                if ((g.count < sensimap.options.group_minsize)&&(sensimap.options.group_redirect)) {
                    sensimap.group_redirect(g);
                } else {
                    //$(this.div).mouseout();
                    sensimap.map.setCenter(this.lonlat, Math.max(sensimap.map.getZoom()+1, sensimap.map.getZoomForExtent(g.bounds)));
                }
                OpenLayers.Event.stop(evt);
                });

        this.events.register("mouseover", this, this.on);
        this.events.register("mouseout", this, this.off);
        },

    on: function(e) {
        //this.listing.addClass('on');
        this.icon.imageDiv.style.backgroundPosition = this.pos_h;
        if (e) OpenLayers.Event.stop(e);
        },

    off: function(e) {
        //this.listing.removeClass('on');
        this.icon.imageDiv.style.backgroundPosition = this.pos;
        if (e) OpenLayers.Event.stop(e);
        }

});

sensimap.GroupMarkers = OpenLayers.Class(OpenLayers.Layer.Markers, {
    initialize: function() {
        OpenLayers.Layer.Markers.prototype.initialize.call(this, "group_markers");
        sensimap.map.addLayer(this);
    },
    hook: function(lon, lat, ad_on_listing) {
        //window.m = this.markers;
        if (!this.markers.length) return false;
        var marker = false, pos = new OpenLayers.LonLat(lon,lat).transform(sensimap.displayProjection, sensimap.map.getProjectionObject());

        for (i in this.markers) {
            var m = this.markers[i];
            if (m.bounds.containsLonLat(pos)) { marker = m; }
            }

        if (!(marker && ad_on_listing)) return; // this shouldn't happen...
        ad_on_listing.hover(function() { marker.on() }, function() {marker.off()});
        return true;
    },
    clearGroups: function() {
        OpenLayers.Layer.Markers.prototype.clearMarkers.call(this);
    },
    addJSON: function(groups, extra) {
         // setup bounds
        var original = []
        for (i in groups) {
            var g = groups[i];
            g.count = parseInt(g.count);
            g.bounds = new OpenLayers.Bounds(g.bounds[0], g.bounds[1], g.bounds[2], g.bounds[3]).transform(sensimap.displayProjection, sensimap.map.getProjectionObject());
            original.push(g.bounds)

            g.bounds = g.bounds.scale(1.1);
            g.center = g.bounds.getCenterLonLat();
            g.centerpoint = new OpenLayers.Geometry.Point(g.center.lon, g.center.lat);
        }
        var gridsize = 0;
        if (groups && groups[0]) {
            gridsize = 1.2 * Math.max(groups[0].bounds.getHeight(), groups[0].bounds.getWidth());
        }

        var merged,tmp_grps, new_grps = [];
        while(groups.length) { // add each group
            var g = groups.pop();
            tmp_grps = [];
            while (new_grps.length) {
                vs = new_grps.pop()
                if (g && (g.bounds.intersectsBounds(vs.bounds))
                    && (g.centerpoint.distanceTo(vs.centerpoint) < gridsize)) {

                        g.bounds.extend(vs.bounds);
                        var sumcount = g.count + vs.count;
                        // avg center (weighted by counts)
                        g.center = new OpenLayers.LonLat((g.center.lon * g.count + vs.center.lon * vs.count)/sumcount,
                                                        (g.center.lat * g.count + vs.center.lat * vs.count)/sumcount);

                        g.centerpoint = new OpenLayers.Geometry.Point(g.center.lon, g.center.lat);
                        g.count = sumcount;
                        groups.push(g); // reinsert it again
                        g = false; // pass the rest to tmp_grps
                } else {
                    tmp_grps.push(vs);
                }
            }
            if(g) {
                tmp_grps.push(g); // if it wasn't merged just add it
            }
            new_grps=tmp_grps;
        }

        this.clearGroups();
        if (sensimap.debug) {
            sensimap.testms.clearMarkers()
        }
        while (new_grps.length) {
            var g_pop = new_grps.pop();
            this.addMarker(new sensimap.GroupMarker(g_pop));
            if (sensimap.debug) {
                sensimap.testms.addMarker(new OpenLayers.Marker.Box(g_pop.bounds, "#080", 1));
            }
            }
        if (sensimap.debug) {
            for (i in original) {
                sensimap.testms.addMarker(new OpenLayers.Marker.Box(original[i], "#800", 1));
            }
        }

        if (extra) {
            extra();
        }
    }
});

