﻿GMap2.prototype.addOverlays = function(a) {
    for (i = 0; i < a.length; i++) {
        try {
            this.overlays.push(a[i]);
            a[i].initialize(this);
            a[i].redraw(true);
        } catch (ex) {
            alert('err: ' + i + ', ' + ex.toString());
        }
    }
    this.reOrderOverlays();
};

// Large map (on homepage)
RXC_MAPTYPE_LARGE = 0;
// Smal map displaying a single house
RXC_MAPTYPE_SINGLE = 1;
// Map for saved ads
RXC_MAPTYPE_SAVED_ADS = 2;
// Map for branches from a particular agent
RXC_MAPTYPE_BRANCHES = 3;


var Homes24Map = {

    // set this to true if you want to see debugging messages in the GLog
    enableDebugging: false,

    // default latitude, longitude (to show the initial map)
    lat: 52.85,

    lng: -4,

    // default zoom level
    zoom: 6,

    // name of the map-container (div)
    mapName: "map",

    // GMap2 object
    map: null,

    icons: new Array(7),

    // width, height
    iconSizes: new Array(
        new GSize(15, 15), // house
        new GSize(23, 20), // school
        new GSize(17, 20), // parking
        new GSize(20, 20), // hotel
        new GSize(16, 20), // hospital
       // new GSize(19, 20), // airport
        new GSize(20, 24) // branch
    ),

    clusterIcons: new Array(7),

    clusterIconSizes: new Array(
        new GSize(24, 24), // house
        new GSize(34, 30), // school
        new GSize(25, 30), // parking
        new GSize(30, 30), // hotel
        new GSize(24, 30), // hospital
        //new GSize(29, 30), // airport
        new GSize(24, 24) // branch    
    ),

    poiTypes: new Array('house', 'school', 'parking', 'hospital', 'hotel', /*'airport',*/ 'branch'), 
    poiNames: new Array("Schools", 'Car Parks', "Hospitals", "Hotels", /*'Airports',*/ "Branches"), 

    poiPositions: new Array(
        new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(100, 73)),
        new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 73)),
       // new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 113)), 
        new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(100, 33)),
        new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 33))
    ),

    poiZooms: new Array(15, 15, 15, 15/*, 7*/), 

    poiControls: new Array(4),  //5
    poiInfos: new Array(4),     //5

    tooltip: null,

    // an info window is opened at the moment
    infoWindowOpen: false,

    // search parameters as a query string
    // might need to be split up into its components for display purposes
    searchParams: null,

    // mapBox can be set externally
    mapBox: null,

    // search is done for an area bigger than the map that we can physically see. 
    // the area for which we have results at the moment
    // type: GLatLngBounds
    outerBounds: null,

    markersCache: null,

    // here we store all properties for JS-clustering
    propertyCache: null,

    imageServer: null,

    locationText: '',

    boundingBox: null,

    locationBounds: null,

    mapType: RXC_MAPTYPE_LARGE,

    initialized: false,

    publication: '',

    filter: 'default',

    needToShowBoundingBox: true,

    // locationBounds could be null
    load: function(publication, sLocationBounds) {
        this.publication = publication;

        if (GBrowserIsCompatible()) {
            this.map = new GMap2($('#' + this.mapName).get(0));
            if (this.mapType == RXC_MAPTYPE_SINGLE) {
                this.loadControls();
            }

            this.map.addControl(new Homes24MapLoadingControl());
            this.map.addControl(new GMapTypeControl());

            this.setPoiControls();

            this.locationBounds = this.initializeLocation(sLocationBounds);
            this.setCenter();

            GEvent.bind(this.map, "click", this, this.debugLocation);
            GEvent.bind(this.map, "zoomend", this, this.onZoomEnd);
            GEvent.bind(this.map, "moveend", this, this.onMoveEnd);
            GEvent.bind(this.map, "move", this, this.onMove);
            GEvent.bind(this.map, "infowindowopen", this, this.onInfoWindowOpen);
            GEvent.bind(this.map, "infowindowclose", this, this.onInfoWindowClose);

            this.tooltip = $.create("div").css('visibility', 'hidden').get(0);
            this.map.getPane(G_MAP_FLOAT_PANE).appendChild(this.tooltip);

            this.markersCache = new Array();

            if (this.mapType == RXC_MAPTYPE_SAVED_ADS) {
                this.propertyCache = new Array();
            }

            this.initializeIcons();
            if (this.needToShowBoundingBox)
                this.showBoundingBox();
        }
    },

    initializeBoundingBox: function(bounds, backgroundcolor, borderColor, opacity) {
        if (this.boundingBox == null) {
            this.boundingBox = new BoundingBox(bounds, backgroundcolor, borderColor, opacity);
            this.map.addOverlay(this.boundingBox);
        }
    },

    showBoundingBox: function() {
        if (this.locationBounds != null)
            this.initializeBoundingBox(this.locationBounds, '#C4DBFF', '#FF8800', 30);
    },

    loadControls: function(nrClusters, foundInRegion) {

        if (!this.initialized) {
            if (this.mapType == RXC_MAPTYPE_LARGE) {
                if (nrClusters > 0) {
                    this.map.enableDoubleClickZoom();
                    this.map.addControl(new GLargeMapControl());
                    this.map.addControl(new GOverviewMapControl());
                }
                else {
                    var control = new Homes24NoResultsControl(foundInRegion,
                         'loc=' + escape(this.locationText) + '&' + this.searchParams);

                    var mapDimensions = this.map.getSize();

                    var offset = new GSize((mapDimensions.width - 230) / 2,
                                            (mapDimensions.height - 70) / 2);

                    this.map.addControl(control, new GControlPosition(G_ANCHOR_TOP_LEFT, offset));
                    this.map.disableDragging();
                }
            }
            else {
                this.map.enableDoubleClickZoom();
                this.map.addControl(new GSmallMapControl());
            }
            this.initialized = true;
        }
    },

    setPoiControls: function() {
        for (var i = 0; i < this.poiInfos.length; i++) {
            this.debug("New poi info [" + this.poiTypes[i + 1] + "] zoom [" + this.poiZooms[i] + "]");
            this.poiInfos[i] = new Homes24PoiInfo(this.poiTypes[i + 1], this.poiZooms[i]);

            if (this.mapType == RXC_MAPTYPE_LARGE) {
                this.poiControls[i] = new Homes24PoiControl(this.poiInfos[i], this.poiNames[i], this.poiPositions[i], this.map);
            }
        }
    },

    showPoiControls: function() {
        if (this.mapType == RXC_MAPTYPE_LARGE) {
            for (var i = 0; i < this.poiControls.length; i++) {
                var control = this.poiControls[i];
                if (this.map.getZoom() >= this.poiInfos[i].zoom) {
                    control.add();
                }
                else {
                    control.remove();
                }
            }
        }
    },

    initializeIcons: function() {
        for (var i = 0; i < this.poiTypes.length; i++) {
            var icon = new GIcon();
            icon.image = '/include/images/maps/' + this.poiTypes[i] + '_detail.gif';
            icon.iconSize = this.iconSizes[i];
            icon.iconAnchor = new GPoint(icon.iconSize.width / 2, icon.iconSize.height / 2);
            icon.infoWindowAnchor = new GPoint(icon.iconSize.width / 2, 6);
            this.icons[i] = icon;

            var clusterIcon = new GIcon();
            clusterIcon.image = '/include/images/maps/' + this.poiTypes[i] + '_cluster.gif';
            clusterIcon.iconSize = this.clusterIconSizes[i];
            clusterIcon.iconAnchor = new GPoint(clusterIcon.iconSize.width / 2, clusterIcon.iconSize.height / 2);
            clusterIcon.infoWindowAnchor = new GPoint(clusterIcon.iconSize.width, 6);
            this.clusterIcons[i] = clusterIcon;
        }
    },

    getIcon: function(iconArray, name) {
        for (var i = 0; i < this.poiTypes.length; i++) {
            if (this.poiTypes[i] == name) return iconArray[i];
        }
    },

    parseBounds: function(sBounds) {
        // N, S, W, E
        var locationParts = sBounds.split(',');
        // GLatLngBounds constructor: SW, NE
        return new GLatLngBounds(
            new GLatLng(locationParts[1], locationParts[2]),
            new GLatLng(locationParts[0], locationParts[3]));
    },

    initializeLocation: function(sLocationBounds) {
        // set the center
        if (sLocationBounds != null && sLocationBounds != '') {
            var locationBounds = this.parseBounds(sLocationBounds);
            var center = locationBounds.getCenter();
            this.lat = center.lat();
            this.lng = center.lng();
            var zoom = this.map.getBoundsZoomLevel(locationBounds);
            this.zoom = zoom;
            return locationBounds;
        }
        return null;
    },

    unload: function() {
        GUnload();
    },

    search: function() {
        if (this.searchParams == null || this.map.getZoom() < 5) { return; }

        // don't bother searching if we already have the data
        if (this.insideOuterBounds()) { return false; }

        this.showLoading();

        this.setOuterBounds();

        var extraParams = {
            zoom: this.map.getZoom(),
            mbox: this.convertBoundsToString(this.outerBounds)
        };

        if (this.locationText != null) {
            extraParams.loc = this.locationText;
        }

        if (this.filter == 'geo') {
            extraParams.filter = 'geo';
        }

        $.post("/" + this.publication + "/mapjax.html", $.param(extraParams) + "&_a=results&" + this.searchParams, $.globalEval);

        return false;
    },

    insideOuterBounds: function() {
        if (this.outerBounds != null) {
            if (this.outerBounds.containsBounds(this.map.getBounds())) {
                return true;
            }
        }
        return false;
    },

    convertBoundsToString: function(bounds) {
        var northEast = bounds.getNorthEast();
        var southWest = bounds.getSouthWest();
        return northEast.lat() + ',' + southWest.lat() + ',' + southWest.lng() + ',' + northEast.lng();
    },

    setSearchParams: function(params) {
        // replace all &amp; by & in params
        this.searchParams = params.replace(/&amp;/g, '&');
    },

    setOuterBounds: function() {
        var innerBounds = this.map.getBounds();

        var innerNE = innerBounds.getNorthEast();
        var innerSW = innerBounds.getSouthWest();

        //var factor = 2;
        var factor = 2;

        var latIncrement = (innerNE.lat() - innerSW.lat()) / factor;
        var lngIncrement = (innerSW.lng() - innerNE.lng()) / factor;

        var outerNE = new GLatLng(innerNE.lat() + latIncrement, innerNE.lng() - lngIncrement);
        var outerSW = new GLatLng(innerSW.lat() - latIncrement, innerSW.lng() + lngIncrement);

        this.outerBounds = new GLatLngBounds(outerSW, outerNE);

        // if the outer bounds change, set all poi controls to unloaded
        for (i = 0; i < this.poiInfos.length; i++) {
            this.poiInfos[i].loaded = false;
        }
    },

    cleanupMarkers: function() {
        // removes markers from the map, that are not in (or near) the current view
        for (var j = 0; j < this.markersCache.length; j++) {
            if (!this.outerBounds.contains(this.markersCache[j].getPoint())) {
                // this.debug('Removing marker at ' + this.markersCache[j].getPoint() );
                this.map.removeOverlay(this.markersCache[j]);
                this.markersCache.splice(j, 1);
                j--;
                // this.debug('Continuing check at ' + j + ', cache contains ' + this.markersCache.length + ' elements');
            };
        };

    },

    addPropertyCacheItem: function(lat, lng, prop) {
        if (this.mapType == RXC_MAPTYPE_SAVED_ADS) {
            var cacheItem = this.getPropertyFromCache(lat, lng);
            if (cacheItem == null) {
                this.propertyCache.push(new Homes24PropertyCacheItem(lat, lng, prop));
            }
            else {
                cacheItem.count++;
            }
        }
    },

    getPropertyFromCache: function(lat, lng) {
        for (var i = 0; i < this.propertyCache.length; i++) {
            var item = this.propertyCache[i];
            if (item.lat == lat && item.lng == lng) {
                return item;
            };
        };

        return null;
    },

    flushPropertyCache: function() {
        for (var i = 0; i < this.propertyCache.length; i++) {
            var item = this.propertyCache[i];

            if (item.count == 1) {
                this.addRealEstateMarker(item.lat, item.lng, item.prop);
            }
            else {
                this.addRealEstateClusterMarker(item.lat, item.lng, item.count, null, item.prop);
            }
        };

        return null;
    },

    // Real estate marker
    addRealEstateMarker: function(lat, lng, prop) {

        if (!this.inMarkersCache('house', lat, lng)) {
            var point = new GLatLng(lat, lng);

            var marker = new GMarker(point, this.icons[0]);

            marker.tooltip = '<div class="tooltip">' + prop.getTooltipText() + '</div>';
            marker.poiType = 'house';

            if (this.mapType == RXC_MAPTYPE_LARGE || this.mapType == RXC_MAPTYPE_SAVED_ADS) {
                GEvent.addListener(marker, "click", $.bind(this, this.showRealEstateInfo, marker, prop));
            }

            GEvent.addListener(marker, "mouseover", $.bind(this, this.showTooltip, marker));
            GEvent.addListener(marker, "mouseout", $.bind(this, this.hideTooltip));

            this.map.addOverlay(marker);
            this.markersCache.push(marker);
        };
    },

    showRealEstateInfo: function(marker, prop) {
        this.hideTooltip();
        var point = marker.getPoint();

        var content = '<div>';

        content += '<div style="font-weight:bold; text-align:center">' + prop.cityName.toUpperCase() + '</div>';

        if (prop.image != '' && prop.image != null) {

            content += '<div style="text-align:center"><img id="prop_img_' + prop.id + '" + width="82" height="61" src="' + this.imageServer + prop.image + '/Image.ashx" onerror="Homes24Map.onImageLoadError(' + prop.id + ')"></div>';
        }

        content += '<div>' + prop.getTooltipText() + '</div>';

        if (this.map.getZoom() < 17) {
            content += '<div><a href="javascript:Homes24Map.zoomIn(' + point.lat() + ', ' + point.lng() + ')">zoom in</a></div>';
        }

        content += '<div>';

        if (this.mapType == RXC_MAPTYPE_SAVED_ADS) {
            content += '<a href="#" onclick="void( Homes24Map.onRealEstateInfoClick(' + prop.id + ') )">';
        }
        else {
            content += '<a href="/' + this.publication + '/doc.html?_a=view&id=' + prop.id + '" onclick="Homes24Map.writeCookie()">';
        }

        content += "view property details</a></div>";
        content += '</div>';

        this.map.openInfoWindowHtml(point, content);
    },

    onImageLoadError: function(prop_id) {
        // try again a few seconds later
        var image = $('#prop_img_' + prop_id).get(0);
        if (image != null && image.src.indexOf("?") == -1) {
            setTimeout('Homes24Map.setImageSource(' + prop_id + ')', 2000);
        }
    },

    setImageSource: function(prop_id) {

        var image = $('#prop_img_' + prop_id).get(0);
        if (image != null && image.src.indexOf("?") == -1) {
            image.src = image.src + "?1";
        }
    },

    onRealEstateInfoClick: function(id) {

        var url = '/' + this.publication + '/doc.html?_a=view&id=' + id;

        if (this.mapType == RXC_MAPTYPE_SAVED_ADS) {

            var mainWindow = window.opener;
            if (mainWindow == null) {
                alert("Sorry, the window from which you opened\n this popup is not open anymore");
            }
            else {
                mainWindow.location = url;
                mainWindow.focus();
            }
        }
        else {
            window.location = url;
        }
    },

    // Real estate cluster marker
    addRealEstateClusterMarker: function(lat, lng, count, box, prop) {

        if (!this.inMarkersCache('house', lat, lng)) {
            var point = new GLatLng(lat, lng);

            this.debug(this.clusterIcons[0].image);

            var marker = new GMarker(point, this.clusterIcons[0]);

            marker.tooltip = '<div class="tooltip">' + count + ' properties in group</div>';
            marker.poiType = 'house';

            GEvent.addListener(marker, "click", $.bind(this, this.showRealEstateClusterInfo, marker, count, box, prop));
            GEvent.addListener(marker, "mouseover", $.bind(this, this.showTooltip, marker));
            GEvent.addListener(marker, "mouseout", $.bind(this, this.hideTooltip, marker));

            this.map.addOverlay(marker);
            this.markersCache.push(marker);
        }
    },



    showRealEstateClusterInfo: function(marker, count, box, prop) {
        this.hideTooltip();
        var point = marker.getPoint();

        var content = '<div>';

        if (prop != null) {
            content += '<div style="font-weight:bold; text-align:center">' + prop.cityName.toUpperCase() + '</div>';
        }

        content += '<div>Found ' + count + ' properties</div>';

        if (box != null) {
            var mboxBounds = this.parseBounds(box);
            content += '<div><a href="/' + this.publication + '/search/?' + this.searchParams +
                        (this.isPointInCurrentLocation(point, mboxBounds) ? ('&loc=' + escape(this.locationText)) : '') +
                       (this.filter == 'geo' ? '&filter=geo' : '') +
                       '&mbox=' + box + '" onclick="Homes24Map.writeCookie()">view these results</a></div>';
        }

        if (this.map.getZoom() < 17) {
            content += '<div><a href="javascript:Homes24Map.zoomIn(' + point.lat() + ', ' + point.lng() + ')">zoom in</a></div>';
        }

        content += '</div>';

        this.map.openInfoWindowHtml(point, content);
    },

    isPointInCurrentLocation: function(point, mboxBounds) {
        return this.locationBounds != null &&
            this.locationBounds.contains(new GLatLng(point.lat(), point.lng())) &&
            this.locationBounds.containsBounds(mboxBounds);
    },

    // Branch marker
    addBranchMarker: function(lat, lng, branch) {

        if (!this.inMarkersCache('branch', lat, lng)) {
            var point = new GLatLng(lat, lng);

            var marker = new GMarker(point, this.icons[6]);

            marker.tooltip = '<div class="tooltip">' + branch.getTooltipText() + '</div>';
            marker.poiType = 'branch';

            GEvent.addListener(marker, "mouseover", $.bind(this, this.showTooltip, marker));
            GEvent.addListener(marker, "mouseout", $.bind(this, this.hideTooltip));

            this.map.addOverlay(marker);
            this.markersCache.push(marker);
        };
    },

    showPoiData: function(poiInfo) {

        this.debug("showPoiData( type:" + poiInfo.poiType + ", show:" + poiInfo.show + ")");

        if (poiInfo.zoom > this.map.getZoom()) {
            this.debug("Don't show POI: zoom level too high");
            return;
        }

        if (poiInfo.show) {

            var insideOuterBounds = this.insideOuterBounds();

            if (!insideOuterBounds || !poiInfo.loaded) {

                if (!insideOuterBounds) this.setOuterBounds();

                // outside outer bounds or not yet loaded                  
                this.getPoiData(poiInfo.poiType);
            }
            else {
                // inside outer bounds + already loaded
                // get them from the cache
                for (var i = 0; i < this.markersCache.length; i++) {
                    var marker = this.markersCache[i];

                    if (marker.poiType == poiInfo.poiType) {

                        this.debug("Show marker " + i);
                        this.map.addOverlay(this.markersCache[i]);
                    }
                }
            }
        }
        else {
            // remove (but not from cache), except if the marker is outside the outer bounds

            for (var i = 0; i < this.markersCache.length; i++) {
                var marker = this.markersCache[i];
                if (marker.poiType == poiInfo.poiType) {

                    if (this.outerBounds != null && !this.outerBounds.contains(marker.getPoint())) {
                        // remove the marker from the cache
                        this.markersCache.splice(i, 1);
                        i--;

                        this.debug("Removed from cache");
                    }

                    marker.added = false;
                    this.map.removeOverlay(marker);
                }
            }
        }

    },
    
    removeMarkersFromMap: function(poiType) {
        for (var i = 0; i < this.poiInfos.length; i++) {
            var poiInfo = this.poiInfos[i];
            if (poiInfo.poiType == poiType) {
                poiInfo.show = false;
                poiInfo.loaded = false;
                for (var i = 0; i < this.markersCache.length; i++) {
                    var marker = this.markersCache[i];
                    if (marker.poiType == poiInfo.poiType) {

                        if (this.outerBounds != null && !this.outerBounds.contains(marker.getPoint())) {
                            // remove the marker from the cache
                            this.markersCache.splice(i, 1);
                            i--;

                            this.debug("Removed from cache");
                        }
                        marker.added = false;
                        this.map.removeOverlay(marker);
                    }
                }
                return;
            }
        }
    },
    
    showPoiDataByType: function(poiType) {
        var info = this.getPoiInfo(poiType);
        info.show = true;
        this.showPoiData(info);
    },

    // show ONLY this POI
    showPoiDataExclusive: function(poiType) {
        for (var i = 0; i < this.poiInfos.length; i++) {
            var info = this.poiInfos[i];
            var nextShow = (info.poiType == poiType);
            if (info.show != nextShow) {
                info.show = nextShow;
                if (info.show && info.zoom > this.map.getZoom()) {
                    alert("Cannot show POIs at this zoom level, please zoom in");
                }
                else {
                    this.showPoiData(info);
                }
            }
        }
    },

    showCheckedPoiData: function() {
        for (var i = 0; i < this.poiInfos.length; i++) {
            this.showPoiData(this.poiInfos[i]);
        }
    },

    getPoiData: function(poiType) {

        this.debug("getPoiData( " + poiType + ")");

        this.showLoading();

        // don't set new outerbounds unless there is none
        if (this.outerBounds == null) {
            this.setOuterBounds();
        }

        var params = {
            _a: 'poi',
            zoom: this.map.getZoom(),
            mbox: this.convertBoundsToString(this.outerBounds),
            type: poiType
        };

        $.post("/" + this.publication + "/mapjax.html", params, $.globalEval);

        return false;
    },

    addPoiMarker: function(poi) {

        this.debug("Adding poi marker");

        var markerInCache = this.getMarkerInCache(poi.type, poi.pos.lat(), poi.pos.lng());

        if (markerInCache == null) {
            var marker = new GMarker(poi.pos, this.getIcon(this.icons, poi.type));
            marker.tooltip = '<div class="tooltip">' + poi.name + '</div>';
            marker.poiType = poi.type;
            marker.added = true;

            if (this.mapType == RXC_MAPTYPE_LARGE) {
                GEvent.addListener(marker, "click", $.bind(this, this.showPoiInfo, marker, poi));
            }

            GEvent.addListener(marker, "mouseover", $.bind(this, this.showTooltip, marker));
            GEvent.addListener(marker, "mouseout", $.bind(this, this.hideTooltip));

            this.map.addOverlay(marker);
            this.markersCache.push(marker);
        }
        else {
            this.debug("Already in cache");

            if (!markerInCache.added) {
                markerInCache.added = true;
                this.map.addOverlay(markerInCache);
            }
        }
    },

    showPoiInfo: function(marker, poi) {
        this.hideTooltip();
        var point = marker.getPoint();

        var content = '<div style="text-align:center">';

        content += '<div style="font-weight:bold;">' + poi.name + '</div>';
        content += '<div>' + poi.phone + '</div>';

        if (this.map.getZoom() < 17) {
            content += '<div><a href="javascript:Homes24Map.zoomIn(' + point.lat() + ', ' + point.lng() + ')">zoom in</a></div>';
        }

        content += '</div>';

        this.map.openInfoWindowHtml(point, content);
    },



    inMarkersCache: function(type, lat, lng) {
        // returns true if we have a marker on this location, in the markercache
        var marker = this.getMarkerInCache(type, lat, lng);

        return (marker != null);
    },

    getMarkerInCache: function(type, lat, lng) {

        for (var i = 0; i < this.markersCache.length; i++) {
            var marker = this.markersCache[i];
            if (marker.poiType == type) {
                var point = marker.getPoint();
                if (point.lat() == lat && point.lng() == lng) {
                    return marker;
                };
            }
        };

        return null;
    },

    showTooltip: function(marker) {

        if (this.infoWindowOpen) return;

        var tooltipEl = $(this.tooltip).html(marker.tooltip);

        var projection = this.map.getCurrentMapType().getProjection();
        var zoom = this.map.getZoom();

        var point = projection.fromLatLngToPixel(this.map.fromDivPixelToLatLng(new GPoint(0, 0), true), zoom);
        var offset = projection.fromLatLngToPixel(marker.getPoint(), zoom);

        var icon = marker.getIcon();
        var anchor = icon.iconAnchor;
        var width = icon.iconSize.width;

        var pos = new GControlPosition(G_ANCHOR_TOP_LEFT,
                    new GSize(offset.x - point.x - anchor.x + width - (tooltipEl.width() / 2),
                              offset.y - point.y - anchor.y - tooltipEl.height() - 2));

        pos.apply(this.tooltip);
        tooltipEl.css("visibility", "visible");
    },

    hideTooltip: function() {
        this.tooltip.style.visibility = "hidden";
    },

    zoomIn: function(lat, lng) {
        this.map.setCenter(new GLatLng(lat, lng), this.map.getZoom() + 1);
    },

    onSearchDone: function(nrClusters, foundInRegion) {
        this.hideLoading();
        this.loadControls(nrClusters, foundInRegion);
        this.showPoiControls();
        this.showCheckedPoiData();
    },

    onPoisLoaded: function(type) {
        var info = this.getPoiInfo(type);
        info.loaded = true;
        this.hideLoading();
    },

    getPoiInfo: function(type) {
        for (i = 0; i < this.poiInfos.length; i++) {
            if (this.poiInfos[i].poiType == type) return this.poiInfos[i];
        }
    },

    showError: function(msg) {
        this.debug("Error! " + msg);
    },

    debugLocation: function(overLay, point) {
        if (point) {
            this.debug("Lat:" + point.lat() + ", Lng:" + point.lng());
        }
    },

    debug: function(msg) {
        if (this.enableDebugging) {
            GLog.write(msg);
        }
    },

    // event handling
    onZoomEnd: function(oldLevel, newLevel) {
        if (this.mapType == RXC_MAPTYPE_LARGE) {
            this.clear();
            this.search();
            if (this.map.getZoom() < 5) {
                this.showPoiControls();
                this.showCheckedPoiData();
            }
        }
        else if (this.mapType == RXC_MAPTYPE_SINGLE) {
            this.clearPois(newLevel);
            //this.clear();
            this.search();
            this.showCheckedPoiData();
        }
        if (this.boundingBox != null) {
            this.map.removeOverlay(this.boundingBox);
            this.boundingBox = null;
            this.showBoundingBox();
        }
    },

    onMoveEnd: function() {
        this.search();
    },

    onMove: function() {
        if (this.boundingBox != null)
            this.boundingBox.redraw(true);
    },

    onInfoWindowOpen: function() {
        this.infoWindowOpen = true;
    },

    onInfoWindowClose: function() {
        this.infoWindowOpen = false;
    },

    // really only done after zoom
    clear: function() {
        this.outerBounds = null;
        this.map.clearOverlays();
        this.markersCache = new Array();

        for (i = 0; i < this.poiInfos.length; i++) {
            this.poiInfos[i].loaded = false;
        }
    },

    clearPois: function(newLevel) {
        this.outerBounds = null;
        for (i = 0; i < this.poiInfos.length; i++) {
            var info = this.poiInfos[i];

            if (info.show && info.loaded && info.zoom > newLevel) {

                for (var j = 0; j < this.markersCache.length; j++) {
                    var marker = this.markersCache[j];
                    if (marker != null && marker.poiType == info.poiType) {
                        marker.added = false;
                        this.map.removeOverlay(marker);
                    }
                }

                info.show = false;
                info.loaded = false;
            }
            var showPoiLink = $('#showPois_' + info.poiType);
            if (showPoiLink.length) {
                if (info.zoom > newLevel) {
                    this.debug("Poi [" + info.poiType + "] zoom: [" + info.zoom + "] new level: [" + newLevel + "]");
                    showPoiLink.hide();
                    $('#' + 'ckb_' + info.poiType).removeAttr("checked");
                }
                else {
                    showPoiLink.show();
                }
            }
        }
    },

    showLoading: function() {
        this.map.disableDragging();
        this.map.disableDoubleClickZoom();
        $('#mapLoading').show();
    },

    hideLoading: function() {
        this.map.enableDragging();
        this.map.enableDoubleClickZoom();
        $('#mapLoading').hide();
    },

    writeCookie: function() {
        var center = this.map.getCenter();
        document.cookie = "RXC_MAP=lat=" + center.lat() + "&lng=" + center.lng() + "&z=" + this.map.getZoom() + ";path=/";
    },

    getCookie: function(name) {
        arg = name + "=";
        alen = arg.length;
        clen = document.cookie.length;
        i = 0;
        while (i < clen) {
            j = i + alen;
            if (document.cookie.substring(i, j) == arg) {
                return this.getCookieVal(j);
            }
            i = document.cookie.indexOf(" ", i) + 1;
            if (i === 0) { break; }
        }
    },

    getCookieVal: function(offset) {
        endstr = document.cookie.indexOf(";", offset);
        if (endstr == -1) { endstr = document.cookie.length; }
        return unescape(document.cookie.substring(offset, endstr));
    },

    setCenter: function() {

        if (this.mapType == RXC_MAPTYPE_LARGE) {
            var mapCookie = this.getCookie("RXC_MAP");

            if (mapCookie != null) {
                var nameValues = mapCookie.split('&');
                for (var i = 0; i < nameValues.length; i++) {
                    var nameValue = nameValues[i].split('=');
                    switch (nameValue[0]) {

                        case 'lat':
                            this.lat = parseFloat(nameValue[1]);
                            break;

                        case 'lng':
                            this.lng = parseFloat(nameValue[1]);
                            break;

                        case 'z':
                            this.zoom = parseInt(nameValue[1]);
                            break;

                    }
                }
            }
        }

        this.map.setCenter(new GLatLng(this.lat, this.lng), this.zoom);
    }

}

// Property object

var Homes24Property = Class.create();

Homes24Property.prototype = {

    id: null,

    cityName: 'default',

    price: '-1',

    image: null,

    prop_type: 'NA',

    prop_type_text: null,

    bedrooms: '0',
    
    priceInstruction: '',
    
    showPrice: false,
    
    showPriceInstruction: false,

    initialize: function(id, cityName, price, image, bedrooms, prop_type, priceInstruction, showPrice, showPriceInstruction, prop_type_text) {
        this.id = id;
        this.cityName = cityName;
        this.price = price;
        this.image = image;
        this.bedrooms = bedrooms;
        this.prop_type = prop_type;
        this.priceInstruction = priceInstruction;
        this.showPriceInstruction = showPriceInstruction;
        this.showPrice = showPrice;
        
        if (prop_type_text == '') {
            this.prop_type_text = 'property';
        }
        else {
            if (prop_type_text != null) {
                this.setPropertyTypeText(prop_type_text);
            }
        }

    },

    getPrice: function() {
        var price = '';
        if(this.showPriceInstruction == 'on')
        {
			price = this.priceInstruction;
			if(this.showPrice)
				price += ' ';
        }
        
        if(this.showPrice)
        {
			price += '£';
			var digitPrice = this.price.replace(/\.\d+$/, '');
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(digitPrice)) {
				digitPrice = digitPrice.replace(rgx, '$1' + ',' + '$2');
			}
			price += digitPrice;
        }
        return price;
    },

    getBedrooms: function() {
        // strip leading zero
        if (this.bedrooms.charAt(0) == '0') return this.bedrooms.charAt(1);
        return this.bedrooms;
    },

    getPropertyType: function() {

        if (this.prop_type_text == null) {

            var searchPropTypeOptions = $('#search_prop_type option');
            if (searchPropTypeOptions.length) {
                var prop_type = this.prop_type.toUpperCase();
                var parentObj = this;
                searchPropTypeOptions.each(function(i, opt) {
                    if (opt.value == prop_type) {
                        // deal with 'Bungalow, Detached'
                        parentObj.setPropertyTypeText(opt.innerHTML);
                    }
                });
            }
            else {
                this.prop_type_text = this.prop_type;
            }
            if (this.prop_type_text == null) {
                this.prop_type_text = "property";
            }
        }
        return this.prop_type_text;
    },

    setPropertyTypeText: function(prop_type_text) {

        var pt_text_parts = prop_type_text.split(/,\s*/);

        if (pt_text_parts.length > 1) {
            var part1 = pt_text_parts[0];
            var part2 = pt_text_parts[1];

            this.prop_type_text = part2 + ' ' + part1;
        }
        else {
            this.prop_type_text = pt_text_parts[0];
        }

    },

    getTooltipText: function() {
        var text = this.getDescription();

        if (text.length > 0) {
            text = text.substring(0, 1).toUpperCase() + text.substring(1);
        }

        if (this.price > 0 && (this.showPriceInstruction == 'on' || this.showPrice)) {
            text += ', ' + this.getPrice();
        }

        return text;
    },

    getDescription: function() {

        var text = "";

        var nrBeds = this.getBedrooms();

        if (nrBeds > 0) {
            text += nrBeds + '-bedroom ';
        }

        var propType = this.getPropertyType().toLowerCase();

        if (propType != 'na') {
            text += this.getPropertyType().toLowerCase();
        }
        else {
            text += 'property';
        }

        return text;

    }

}


var Homes24Branch = Class.create();

Homes24Branch.prototype = {

    name: null,

    initialize: function(name) {
        this.name = name;
    },

    getTooltipText: function() {
        return this.name;
    }

}

// Poi object

var Homes24Poi = Class.create();

Homes24Poi.prototype = {

    type: null,
    name: '',
    phone: '',
    pos: null,

    initialize: function(type, name, lat, lng) {
        this.type = type;

        var nameParts = name.split('-');

        if (nameParts.length > 1) {
            this.phone = nameParts[1];
        }

        this.name = nameParts[0];

        this.pos = new GLatLng(lat, lng);
    }
}

var Homes24PoiInfo = Class.create();

Homes24PoiInfo.prototype = {
    zoom: 1,
    poiType: null,
    show: false,

    initialize: function(poiType, zoom) {
        this.poiType = poiType;
        this.zoom = zoom;
    }
}


var Homes24PropertyCacheItem = Class.create();

Homes24PropertyCacheItem.prototype = {

    lat: 0,
    lng: 0,
    count: 1,
    prop: null,

    initialize: function(lat, lng, prop) {
        this.lat = lat;
        this.lng = lng;
        this.prop = prop;
    }
}

// PoiControl

function Homes24PoiControl(poiInfo, name, position, map) {
    this.name = name;
    this.poiInfo = poiInfo;
    this.position = position;
    this.map = map;
    this.added = false;
}

Homes24PoiControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
Homes24PoiControl.prototype.initialize = function(map) {

    if (!this.container) {
        var divContainer = $.create("div").attr('class', "poiControl");

        this.checkbox = $.create("input").attr('type', 'checkbox').get(0);
        this.checkbox.onclick = $.bind(this, this.onClick);
        divContainer.append(this.checkbox);

        divContainer.append($.create("span").text(this.name));

        this.container = divContainer.get(0);
    }

    map.getContainer().appendChild(this.container);
    return this.container;
}

Homes24PoiControl.prototype.onClick = function() {
    this.poiInfo.show = this.checkbox.checked;
    Homes24Map.showPoiData(this.poiInfo);
}

Homes24PoiControl.prototype.add = function() {
    if (!this.added) {
        this.map.addControl(this);
        this.added = true;
    }
}

Homes24PoiControl.prototype.remove = function() {
    if (this.added) {
        this.map.removeControl(this);
        this.added = false;
    }
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
Homes24PoiControl.prototype.getDefaultPosition = function() {
    return this.position;
}

function Homes24NoResultsControl(foundInRegion, searchParams) {

    this.container = document.createElement("div");
    this.container.id = "gradBack2";

    var par1 = document.createElement("p");
    par1.appendChild(document.createTextNode("Sorry, there are no suitable properties matching your exact location requirements."));

    this.container.appendChild(par1);

    var par2 = document.createElement("p");
    if (foundInRegion > 0) {

        var lf = document.createElement('br')
        var link = document.createElement("a");
        link.href = "/property/search.html?_a=map_results&" + searchParams + "&filter=geo";
        link.appendChild(lf)
        link.appendChild(document.createTextNode("Click here"));

        par2.appendChild(document.createTextNode("However, there may be suitable alternatives in the immediate vicinity."));
        par2.appendChild(link);

    }
    else {
        par2.appendChild(document.createTextNode("Please try a new search."));
    }

    this.container.appendChild(par2);
}

Homes24NoResultsControl.prototype = new GControl();

Homes24NoResultsControl.prototype.initialize = function(map) {
    map.getContainer().appendChild(this.container);
    return this.container;
}

Homes24NoResultsControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(100, 100))
}

function Homes24MapLoadingControl() { }

Homes24MapLoadingControl.prototype = new GControl();

Homes24MapLoadingControl.prototype.initialize = function(map) {

    if (!this.container) {

        var containerDiv = $.create("div").css('display', 'none').attr('class', "contentBox").attr('id', 'mapLoading');
        var top = $.create("div").attr('class', "Top");
        var middle = $.create("div").attr('class', "Middle");
        var bottom = $.create("div").attr('class', "Bottom");

        containerDiv.append(middle);
        middle.append(top);
        top.append(bottom);

        bottom.append($.create("img").attr('src', "/include/images/rotating_arrow.gif"));
        bottom.append($.create("span").attr('id', "mapLoadingSpan").text("Loading locations..."));

        this.container = containerDiv.get(0);
    }

    map.getContainer().appendChild(this.container);
    return this.container;
}


// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
Homes24MapLoadingControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(8, 35));
}
