/**
 * @desc:    Google Places
 *
 * @site:    Real Estate Solution
 * @author:    Michael Pope
 *            nyndesigns.com
 *
 * @file:    google_places.js
 * @date:    19.04.2013 - 16:23:52  [Michael Pope]
 */

var rs_google_places = {
    enabled: false,
    cached_results: {},
    markers: [],
    map: {
        object: null,
        id: '',
        radius: 0,
        center: {
            latitude: 0,
            longitude: 0,
        }
    },
    list: {
        object: null,
        enabled: false,
        type: 'table',
        id: '',
        scroll_into_view: {
            enabled: true,
            offset: 85,
            speed: 800,
        },
    },
    zoom: 12,
    search: {
        latitude: 0,
        longitude: 0,
        position: 0,
        radius: 5000,
        type: '',
        types: {
            grocery_or_supermarket: ['grocery_or_supermarket'],
            school: ['school'],
            restaurant: ['restaurant'],
            gas_station: ['gas_station'],
            bank: ['bank'],
            park: ['park'],
            transit: ['subway_station', 'bus_station', 'transit_station'],
            police: ['police'],
            fire_station: ['fire_station'],
            place_of_worship: ['church', 'mosque', 'hindu_temple', 'synagogue'],
            hospital: ['hospital'],
            entertainment: ['amusement_park', 'aquarium', 'art_gallery', 'bowling_alley', 'casino', 'zoo', 'spa', 'night_club', 'museum'],
            health: ['doctor', 'dentist', 'pharmacy', 'hospital', 'physiotherapist', 'drugstore', 'veterinary_care']
        },
        service: {
            object: null,
            type: 'nearby'
        },
        caching_enabled: true
    },
    request: {},
    icons: {},

    /**
     * Initialize
     */
    initalize: function (settings)
	{
        if ($.isPlainObject(rs_google_places)) {
            $.extend(true, rs_google_places, settings);
        }

        if (
            rs_google_places.list.id != '' &&
            $('#' + rs_google_places.list.id).length > 0
        ) {
            rs_google_places.list.object = $('#' + rs_google_places.list.id).first();
            rs_google_places.list.enabled = true;
        } else {
            rs_google_places.list.enabled = false;
        }

        if (rs_google_places.map.id != '') {
            rs_google_places.map.object = $(document).data('map' + rs_google_places.map.id);
            if (rs_google_places.map.object != null) {
                if (typeof resProperty != "undefined" && resProperty.geometry && resProperty.geometry.location && typeof resProperty.geometry.location === 'object') { // note: should not cause issues to use source; but required for backwards compat
                    rs_google_places.search.position = resProperty.geometry.location;
                } else if (typeof property != "undefined" && property.geometry && typeof property.geometry.location && typeof property.geometry.location === 'object') { // note: should not cause issues to use source; but required for backwards compat
                    rs_google_places.search.position = property.geometry.location;
                } else if (!rs_google_places.search.position && rs_google_places.search.latitude && rs_google_places.search.longitude) {
                    rs_google_places.search.position = new google.maps.LatLng(rs_google_places.search.latitude, rs_google_places.search.longitude);
                }

                if (rs_google_places.search.position) {
                    rs_google_places.enabled = true;
                    google.maps.Marker.prototype.setPlace = function (place) {	// extend marker to allow for place object to be passed
                        this.place = place;
                    };
                    return true;
                }
            }
        }

        return false;
    },

    /**
     * Google places request
     */
    make_request: function ()
	{
        if (!(rs_google_places.enabled && rs_google_places.search.type in rs_google_places.search.types)) {
            return;
        }

        rs_google_places.map.object.setZoom(rs_google_places.zoom);
        if (rs_google_places.search.caching_enabled && (rs_google_places.search.type in rs_google_places.cached_results)) {
            rs_google_places.add_markers(true);
            if (rs_google_places.list.enabled) {
                rs_google_places.create_list(true);
            }
            return;
        }

        if (rs_google_places.search.service.object == null) {
            rs_google_places.search.service.object = new google.maps.places.PlacesService(rs_google_places.map.object);
        }

        switch (rs_google_places.search.service.type) {
            case 'nearby':
                var request = {
                    location: rs_google_places.search.position,
                    radius: rs_google_places.search.radius,
                    types: rs_google_places.search.types[rs_google_places.search.type]
                };
                rs_google_places.search.service.object.nearbySearch($.extend(request, request, rs_google_places.request), rs_google_places.request_callback);
                break;
        }
    },

    /**
     * Google places request callback
     */
    request_callback: function (results, status)
	{
        if (status == google.maps.places.PlacesServiceStatus.OK) {
            rs_google_places.cached_results[rs_google_places.search.type] = results;
            rs_google_places.add_markers(true);
            if (rs_google_places.list.enabled) {
                rs_google_places.create_list(true);
            }
        } else if (status == google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
            rs_google_places.clear_markers();
            if (rs_google_places.list.enabled) {
                rs_google_places.add_no_list_items_message(true);
            }
        } else if (
            status == google.maps.places.PlacesServiceStatus.OVER_QUERY_LIMIT ||
            status == google.maps.places.PlacesServiceStatus.REQUEST_DENIED
        ) {
            rs_google_places.clear_markers();
            rs_google_places.enabled = false;
            if (rs_google_places.list.enabled) {
                rs_google_places.add_no_list_items_message(true);
            }
        }
    },

    /**
     * Clear existing google places markers from map
     */
    clear_markers: function ()
	{
        for (var i = 0; i < rs_google_places.markers.length; i++) {
            google.maps.event.clearInstanceListeners(rs_google_places.markers[i]);
            rs_google_places.markers[i].setMap(null);
        }
        rs_google_places.markers = [];
        $('#' + rs_google_places.map.id).trigger('single_info_window');
    },

    /**
     * Add google places markers to map
     *
     * @param    bool    clear existing google places markers first
     */
    add_markers: function (clear_markers)
	{
        if (clear_markers == true) {
            rs_google_places.clear_markers();
        }
        for (var i = 0; i < rs_google_places.cached_results[rs_google_places.search.type].length; i++) {
            rs_google_places.markers.push(rs_google_places._add_marker(rs_google_places.cached_results[rs_google_places.search.type][i]));
        }
    },

    /**
     * Set Search Type
     *
     * @param    string    type
     * @return    bool
     */
    set_search_type: function (type)
	{
        if (!(type in rs_google_places.search.types) || rs_google_places.search.type == type) {
            return false;
        }
        rs_google_places.search.type = type;
        return true;
    },

    /**
     * Add google places marker to map
     *
     * @param    object    google place
     */
    _add_marker: function (place)
	{
        if (rs_google_places.search.type in rs_google_places.icons) {
            var map_marker = rs_google_places.icons.type;
        } else {
            var map_marker = new google.maps.MarkerImage(place.icon, null, null, null, new google.maps.Size(25, 25));
        }
        var new_marker = new google.maps.Marker({
            map: rs_google_places.map.object,
            position: place.geometry.location,
            title: place.name,
            icon: map_marker,
            place: place
        });
        var infowindow = new google.maps.InfoWindow({
            content: rs_google_places._format_marker_content(place)
        });
        google.maps.event.addListener(new_marker, 'click', function () {
            if (infowindow) {
                $('#' + rs_google_places.map.id).trigger('single_info_window');
            }
            infowindow.open(rs_google_places.map.object, new_marker);
            rs_google_places.map.object.set('center', place.geometry.location);
        });
        $('#' + rs_google_places.map.id).bind('single_info_window', function () {
            infowindow.close();
        });
        return new_marker;
    },

    /**
     * Format Place content for marker info window presentation
     *
     * @param    object google place
     */
    _format_marker_content: function (place)
	{
        var content_open = '<table class="map_amenity_info_window">';
        var content_close = '</table>';
        var icon = place.icon;
        var icon_title = place.name;
        return content_open +
            '<tr>' +
            '<td><i class="map_amenity_icon map-icon-' + place.types[0].replace('_', '-') + '"></i><img src="' + icon + '" class="map_amenity_image" title="' + icon_title + '" /></td>' +
            '<td><span class="map_amenity_title">' + place.name + '</span></td>' +
            '</tr>' +
            content_close;
    },

    /**
     * Clear google places to list
     */
    clear_list: function ()
	{
        rs_google_places.list.object.html('').hide();
    },

    /**
     * Create google places to list
     *
     * @param    bool    clear existing google places from list
     */
    create_list: function (clear_list)
	{
        if (clear_list == true) {
            rs_google_places.clear_list();
        }

        rs_google_places.list.object.html('<' + rs_google_places.list.type + '></' + rs_google_places.list.type + '>');

        var ordered_markers = rs_google_places.markers.sort(rs_google_places._sort_markers)
        for (var i = 0; i < ordered_markers.length; i++) {
            var class_name = 'item_' + i;
            rs_google_places._add_list_item(ordered_markers[i], class_name);
        }

        rs_google_places.list.object.show();
    },

    /**
     * Add place to list
     *
     * @param    object    map marker
     * @param    string    class
     */
    _add_list_item: function (marker, class_name)
	{
        rs_google_places.list.object.find('> ' + rs_google_places.list.type).append(rs_google_places._format_list_content(marker, class_name));
        $('.' + class_name).click(function () {
            google.maps.event.trigger(marker, 'click');
            if (rs_google_places.list.scroll_into_view.enabled) {
                if (rs_google_places.list.scroll_into_view.speed <= 0) {
                    $('html, body').scrollTop($('#' + rs_google_places.map.id).offset().top - rs_google_places.list.scroll_into_view.offset);
                } else {
                    $('html, body').animate({scrollTop: $('#' + rs_google_places.map.id).offset().top - rs_google_places.list.scroll_into_view.offset}, rs_google_places.list.scroll_into_view.speed);
                }
            }
        });
    },

    /**
     * Format place content for place list presentation
     *
     * @param    object    map marker
     * @param    string    class
     */
    _format_list_content: function (marker, class_name)
	{
        if (typeof marker.icon == 'object') {
            var icon = marker.icon.url;
        } else {
            var icon = marker.icon;
        }
        var icon_title = marker.title;
        var distance = google.maps.geometry.spherical.computeDistanceBetween(rs_google_places.search.position, marker.position);
        return '<tr class="' + class_name + '">' +
            '<td><i class="map_amenity_list_icon map-icon-' + marker.place.types[0].replace('_', '-') + '"></i><img src="' + icon + '" class="map_amenity_list_image" title="' + icon_title + '" /></td>' +
            '<td><span class="map_amenity_list_title">' + marker.title + '</span></td>' +
            '<td>' + (distance < 100 ? distance.toFixed(1) + ' meters' : (distance / 1000).toFixed(1) + ' km') + '</td>' +
            '</tr>';
    },

    /**
     * Add no list items message
     *
     * @param    bool    clear existing google places from list
     */
    add_no_list_items_message: function (clear_list)
	{
        if (clear_list == true) {
            rs_google_places.clear_list();
        }
        search_types = rs_google_places.search.type.split('_');
        for (i = 0; i < search_types.length; i++) {
            search_types[i] = search_types[i].charAt(0).toUpperCase() + search_types[i].slice(1);
        }
        rs_google_places.list.object.html(rs_google_places._format_no_list_items_message(search_types.join(' '))).show();
    },

    /**
     * Format no places message
     *
     * @param    string    search type
     */
    _format_no_list_items_message: function (search_type)
	{
        return '<h3 class="map_amenity_empty_list">' +
            '<span class="map_amenity_search_type">' +
            search_type +
            '</span>' +
            ': We could not find any nearby matches.' +
            '</h3>';

    },

    /**
     * Sort Markers
     *
     * @param    object    marker_a
     * @param    object    marker_b
     * @return    float
     */
    _sort_markers: function (marker_a, marker_b)
	{
        return google.maps.geometry.spherical.computeDistanceBetween(rs_google_places.search.position, marker_a.position) -
            google.maps.geometry.spherical.computeDistanceBetween(rs_google_places.search.position, marker_b.position)
    },

    /**
     * Calculate radius of viewable map
     *
     * @return int
     */
    calculate_radius: function ()
	{
        var bounds = rs_google_places.map.object.getBounds();

        var sw = bounds.getSouthWest();
        //var ne = bounds.getNorthEast();

        var lat1 = sw.lat();
        var lon1 = sw.lng();

        rs_google_places.map.center.latitude = rs_google_places.map.object.getCenter().lat();
        rs_google_places.map.center.longitude = rs_google_places.map.object.getCenter().lng();

        var R = 6371; // km
        var dLat = (rs_google_places.map.center.latitude - lat1).toRad();
        var dLon = (rs_google_places.map.center.longitude - lon1).toRad();

        var lat1 = lat1.toRad();
        var lat2 = rs_google_places.map.center.latitude.toRad();

        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);

        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        rs_google_places.map.radius = R * c;

        return rs_google_places.map.radius;
    }
};

(function ($) {
    $(document).ready(function () {
        /**
         * Map Amenities
         */
        $('.map_amenities div.map_amenity')
			.click(function () {
				$('.map_amenities div.map_amenity').removeClass('ma_active');
				$(this).addClass('ma_active');
				if (rs_google_places.set_search_type($(this).attr('id'))) {
					rs_google_places.make_request();
				}
			});
    });
})(jQuery);
