/**
 * Description
 * @author tjunghans, Namics AG, www.namics.com
 * @class map
 * @namespace MEDELA.locationfinder.gmap
 * @requires jQuery
 */

/* JSLint Stuff */
/*globals jQuery, NX, G_MAP_FLOAT_PANE, G_DEFAULT_ICON, GMap2, GEvent, GLatLng, GLatLngBounds, GMarker, GPoint, GIcon, GSize, MarkerClusterer, MarkerManager */

var MEDELA = MEDELA || {};
MEDELA.locationfinder = MEDELA.locationfinder || {};
MEDELA.locationfinder.gmap = MEDELA.locationfinder.gmap || {};

(MEDELA.locationfinder.gmap.map = function () {

    var bindings = null,
        clearMarkers = null,
        dataObject = null,
        init = null,
        $map = null,
        getMap = null,
        createMap = null,
        createMedelaIcon = null,
        createClusteredMarkers =  null,
        getCountryByMarker = null,
        getDataObject = null,
        getLocationId = null,
        getLocationData = null,
        getMinMaxLatLng = null,
        infoWindowOverlay = null,
        markerClstrr = null,
        markerMngr = null,
        rentAndOrBuyLabel = null,
        resetInfowindowPane = null,

        /**
         * When the ajax request of the search is started <strong>searchInProgress</strong> is set to true.
         * @property {Boolean} searchInProgress
         */
        searchInProgress = false,
        setDataObject = null,
        setMapInititalPosition = null,
        self = this,
        createInfowin = null,
        MARKER_MAX_ZOOM = 14;

    /**
     * Gets the hardcoded latlng and zoom level and applies these to map
     *
     * @method setMapInititalPosition
     * @param {Gmap2} gmap
     * @requires MEDELA.locationfinder.parameters
     */
    setMapInititalPosition = function (map) {
        var viewPortLat = 0,
            viewPortLng = 0,
            viewPortZoomLevel = 0;

        viewPortLat = MEDELA.locationfinder.parameters.getValue('viewPortLat');
        viewPortLng = MEDELA.locationfinder.parameters.getValue('viewPortLng');
        viewPortZoomLevel = parseInt(MEDELA.locationfinder.parameters.getValue('viewPortZoomLevel'), 10);

        map.setCenter(new GLatLng(viewPortLat, viewPortLng), viewPortZoomLevel);
    };

    /**
     * The "construct" of this singleton. The order of properties and methods used is important.
     *
     * @method init
     */
    init = function () {
        var dataObj = null,
            locations = null;

        $map = jQuery('#medelaLocationfinderGmap');

        createMap();

        // set events
        bindings();

        // position map
        // http://itouchmap.com/latlong.html
        setMapInititalPosition(getMap());

        // use default UI elements for gmap
        getMap().setUIToDefault();

        getMap().disableScrollWheelZoom();

        // get initial data
        // jsonData is hardcoded in template
        dataObj = MEDELA.locationfinder.data(MEDELA.locationfinder.jsonData);
        locations = dataObj.getLocationArray();

        // create markers
        // third param is false, because we are using the hardcoded data (jsonData)
        createClusteredMarkers(getMap(), locations, false);

    };


    /**
     * Retreives the min and max values for latitude and longitude of all markers
     * @param {Array} data (same as location parameter of createClusteredMarkerscreateClusteredMarkers)
     * @return {Object} minLat, maxLat, minLng, maxLng
     */
    getMinMaxLatLng = function (data) {
        var i = 0,
            minLat = 0,
            maxLat = 0,
            minLng = 0,
            maxLng = 0,
            curLng = 0,
            curLat = 0,
            multiplicator = 1000000;

        for (i = 0; i < data.length; i++) {
            curLng = data[i].location.lng * multiplicator;
            curLat = data[i].location.lat * multiplicator;

            if (0 === i) {
                minLat = maxLat = curLat;
                minLng = maxLng = curLng;
            }

            if (curLng > maxLng) {
                maxLng = curLng;
            } else if (curLng < minLng) {
                minLng = curLng;
            }

            if (curLat > maxLat) {
                maxLat = curLat;
            } else if (curLat < minLat) {
                minLat = curLat;
            }
        }

        return {
            'minLat' : minLat / multiplicator,
            'maxLat' : maxLat / multiplicator,
            'minLng' : minLng / multiplicator,
            'maxLng' : maxLng / multiplicator
        };

    };



    /**
     * Creates the markers using the MarkerClusterer Library.
     *
     * @method createClusteredMarkers
     * @param {GMap} gmap
     * @param {Array} locations
     * @param {Boolean} fromSearch (true if data from search, false if from page load)
     */
    createClusteredMarkers = function (gmap, locations, fromSearch) {

        var i = 0,
            coords = null,
            location = null,
            marker = null,
            markerBatch = [],
            newBounds = null,
            point = null,
            styles = null;

		if (fromSearch !== false) {
			// coords holds the min max lng lat values which create a rectangle
	        coords = getMinMaxLatLng(locations);

	        // newBounds is the new GLatLngBounds object created from the coords values
	        newBounds = new GLatLngBounds(new GLatLng(coords.minLat, coords.minLng), new GLatLng(coords.maxLat, coords.maxLng));

	        // Zoom in and center map to fit the newBounds box
	        gmap.setCenter(newBounds.getCenter(), gmap.getBoundsZoomLevel(newBounds));
		}


        for (i = locations.length - 1; i >= 0; --i) {
            location = locations[i].location;

            point = new GLatLng(location.lat, location.lng);

            if (typeof location.alphaLetter !== 'undefined') {
                marker = new GMarker(point, {
                    'icon' : createMedelaIcon(location.alphaLetter)
                });
            } else {
                marker = new GMarker(point, {
                    'icon' : createMedelaIcon()
                });
            }

            marker.medelaLocation = location;

            // add marker object to locationdata here
            if (fromSearch === true) {
                getDataObject().addMarkerObject(i, marker);
            }
            markerBatch.push(marker);

        }

        styles = [{
            url: '/images/medela-pin-medela-shadow.png',
            height: 40,
            width: 46,
            opt_anchor: [0, 40],
            'cssClass' : 'clusterMarker',
            'noText' : true
        }];

        markerClstrr = new MarkerClusterer(gmap, markerBatch, {
            'maxZoom' : MARKER_MAX_ZOOM,
            'gridSize' : 50,
            'styles' : styles
        });

        markerClstrr.resetViewport();

    };

    /**
     * Removes all markers created by the marker manager or clusterer
     * @method clearMarkers
     */
    clearMarkers = function () {
        if (markerMngr) {
            markerMngr.clearMarkers();
        }

        if (markerClstrr) {
            markerClstrr.clearMarkers();
        }
    };

    /**
     * Return GMap object
     * @method getMap
     */
    getMap = function () {
        return self.map;
    };

    /**
     * Instantiates Gmap object
     * @method createMap
     */
    createMap = function () {
        self.map = new GMap2($map.get(0));
    };

    /**
     * Creates a generic Medela Marker
     *
     * @method createMedelaIcon
     * @param ?letter
     */
    createMedelaIcon = function () {
        var medelaIcon = null,
            medelaIconSize = null,
            alphaLetter = null;

        if (arguments.length > 0) {
            alphaLetter = arguments[0];
        }

        medelaIcon = new GIcon(G_DEFAULT_ICON);

        if (alphaLetter !== null) {
            medelaIcon.image = '/images/medela-pin-' + alphaLetter + '.png';
        } else {
            medelaIcon.image = '/images/medela-pin-blank.png';
        }

        medelaIcon.shadow = '/images/medela-pin-shadow.png';

        medelaIconSize = new GSize(46, 40);
        medelaIcon.iconSize = medelaIconSize;
        medelaIcon.shadowSize = medelaIconSize;

        medelaIcon.iconAnchor = new GPoint(0, 40);

        return medelaIcon;
    };

    getLocationId = function (marker) {
        return marker.medelaLocation.id;
    };

    // TODO: tjunghans - getLocationData is not an apropriate name anymore
    /**
     * @method getLocationData
     * @param {Object} marker
     * @param {Boolean} ?fromSearch option, if set true, the data will be retreived from marker.medelaLocation
     */
    getLocationData = function (marker) {


        if (arguments.length > 1 && arguments[1] === true) {
            createInfowin(marker, marker.medelaLocation);
            return true;
        }
       
        
        var locationId = getLocationId(marker),
        locationData = null;

        jQuery.ajax({
            'type': 'GET',
            'url': document.location.href.split("#")[0] + '/' +  locationId + '.json',
            'dataType' : 'json',
            'beforeSend' : function () {
                searchInProgress = true;
            },
            'success': function (data) {

               // resetInfowindowPane();
                createInfowin(marker, data.location);

                //FIXME (DS): little hack with hide
                //jQuery("#tmp").hide();

            },
            'complete' : function () {
                searchInProgress = false;
            }
        });

        return true;

    };

    resetInfowindowPane = function () {
        // this clears entire infowindow pane
        getMap().getPane(G_MAP_FLOAT_PANE).innerHTML = '';
    };

    /**
     * Helper function for returning the correct label
     * @method rentAndOrBuyLabel
     * @param data
     * @returns {String} either 'rent', 'buy' or 'rent_and_buy' are returned
     */
    rentAndOrBuyLabel = function (data) {
        var dataRentAndBuy = data.rent_and_buy,
            dataBuy = data.buy;

        if (dataRentAndBuy === true) {
            return 'rent_and_buy';
        } else if (dataBuy === true) {
            return 'buy';
        } else {
            return 'rent';
        }
    };

    /**
     * Finds the country name in the marker data. The data structure returned by search result
     * differs from initial data, which is why the country name comes from different locations.
     * @method getCountryByMarker
     * @param {GMarker} marker
     * @return {String} country name or {String} '', if nothing was found
     */
    getCountryByMarker = function (marker) {
        if (marker.medelaLocation.cou) {
            return marker.medelaLocation.cou;
        } else if (marker.medelaLocation.country) {
            return marker.medelaLocation.country.country.name;
        } else {
            return '';
        }
    };

    createInfowin = function (marker, data) {

        /**
         * Holds temporary HTMLFormElement infowin. For performance reasons, tmpHtml is an array and each line an item
         * @property tmpHtml
         */
        var tmpHtml = [],

        /**
         * When tmpHtml is concatenated to a string, the result (string) is saved in iwHtml
         * @property iwHtml
         */
        iwHtml = '',
        lang = null,
        googleMapUrl = '',
        item = null;

        // Retreive translation labels
        lang = new MEDELA.locationfinder.InfowindowLabels(data.labels);

        // Get Google Map URL
        // TODO: This should only be done once for each market/country.
        googleMapUrl = MEDELA.locationfinder.getGoogleMapUrlByCountry(getCountryByMarker(marker));


        // Create Infowindow HTML
        tmpHtml.push('<div id="infowin" class="infowin">');
            tmpHtml.push('<div class="wrapper">');
                tmpHtml.push('<div class="infowin-top"></div>');
                tmpHtml.push('<div class="infowin-middle">');
                    tmpHtml.push('<div class="infowin-content">');
                        tmpHtml.push('<div class="content-group vcard">');
                            tmpHtml.push('<h2>' + data.name + '</h2>');
                            tmpHtml.push('<span class="fn org">' + data.name + '</span>');
                            tmpHtml.push('<div class="adr"><span class="street-address">' + data.street_address + '</span>');
                                tmpHtml.push('<div>');
                                    tmpHtml.push('<span class="postal-code">' + data.zip + '</span>');
                                    tmpHtml.push('<span class="locality">' + data.city + '</span>');
                                tmpHtml.push('</div>');
                            tmpHtml.push('</div>');

                        if (data.phone != null && data.phone.length > 0) {
                            tmpHtml.push('<div class="tel">');
                                tmpHtml.push('<span class="type">work</span>');
                                tmpHtml.push('<span class="label">' + I18n.t('flyout.phone') + ':</span> <span class="value">' + data.phone + '</span>');
                            tmpHtml.push('</div>');
                        }

                        if (data.email_address !== null && data.email_address.length > 0) {
                            tmpHtml.push('<div class="email">');
                                tmpHtml.push('<span class="type">internet</span>');
                                tmpHtml.push('<span class="label">' + I18n.t('flyout.email') + ':</span> <a href="#" class="value">' + data.email_address + '</a>');
                            tmpHtml.push('</div>');
                            
                        }

                            if (data.internet_address !== null && data.internet_address.length > 0) {
                                tmpHtml.push('<div>');
                                tmpHtml.push('<span class="label">' + I18n.t('flyout.internet') + ':</span> <a href="http://' + data.internet_address + '" class="url">' + data.internet_address + '</a>');
                                tmpHtml.push('</div>');
                            }

                            if (data.shop_hours !== null && data.shop_hours.length > 0) {
                                tmpHtml.push('<div>');
                                tmpHtml.push('<span class="label">' + I18n.t('flyout.shop_hours') + ':</span> <span class="value">' + data.shop_hours + '</span>');
                                tmpHtml.push('</div>');
                            }
                        tmpHtml.push('</div>');
                        tmpHtml.push('<div class="content-group">');
                            tmpHtml.push('<h3>' + I18n.t('flyout.offer') + ':</h3>');
                            tmpHtml.push('<ul>');

                            for (item in data) {
                                if (data[item] === true && (item.search(/rent/) === -1 && item.search(/buy/) === -1)) {
                                    tmpHtml.push('<li><span>' + lang.getValueOfLabel(item) + '</span></li>');
                                }
                            }

                            tmpHtml.push('</ul>');
                        tmpHtml.push('</div>');
                        tmpHtml.push('<div class="content-group">');
                            tmpHtml.push('<h3>' + lang.getValueOfLabel(rentAndOrBuyLabel(data)) + '</h3>');
                        tmpHtml.push('</div>');
                        tmpHtml.push('<div class="content-group infowin-controls">');
                            tmpHtml.push('<a href="http://' + googleMapUrl + '/maps?f=d&amp;t=m&amp;saddr=&amp;daddr=' + data.city + ',' + data.zip + ',' + data.street_address + '&amp;hl=de&amp;ie=UTF8&amp;z=6" id="routeplan-toggler">' + I18n.t('flyout.route_planning') + '</a>');
                            tmpHtml.push('<a href="#" id="page-printer">' + I18n.t('flyout.print') + '</a>');
                        tmpHtml.push('</div>');
                    tmpHtml.push('</div>');
                tmpHtml.push('</div>');
                tmpHtml.push('<div class="infowin-bottom">');
                tmpHtml.push('</div>');
                tmpHtml.push('<div class="infowin-controls"><a href="#" id="infowin-close">' + I18n.t('flyout.close') + '</a></div>');
            tmpHtml.push('</div>');


        iwHtml = tmpHtml.join("");


        // save infowindow html to marker object to save ajax requests
        marker.medelaInfowinHtml = iwHtml;

        // clear window pane
        resetInfowindowPane();

        // create infowindow overlay
        infoWindowOverlay = MEDELA.locationfinder.gmap.infowindow(marker, iwHtml);

        getMap().addOverlay(infoWindowOverlay);

        // The info window gets cloned into a hidden div, which is made visible on print
        jQuery('#medelaLocationfinder-print').html('<div class="infowin">' + jQuery('#infowin').html() + '</div>');

       // ignore map zoom when dblclick on flyout
      GEvent.addDomListener(document.getElementById('infowin'), 'dblclick', function (e) {
        var agent = jQuery.browser;
        if(agent.msie) {
            e.cancelBubble = true;
        } else {
            e.stopPropagation();
        }
       });
        

        // Event listener for closing the current infowindow
        GEvent.addDomListener(document.getElementById('infowin-close'), 'click', function () {
            getMap().removeOverlay(infoWindowOverlay);
            return false;
        });

        // Event listener for printing
        GEvent.addDomListener(document.getElementById('page-printer'), 'click', function () {
            window.print();
            return false;
        });

      
    };

    setDataObject = function (dataObj) {
        dataObject = dataObj;
    };

    getDataObject = function () {
        return dataObject;
    };

    /**
     * Holds all event handles
     * @method bindings
     */
    bindings = function () {

        jQuery(window).resize(function () {
            getMap().checkResize();
        });

        // Hide the infowindow when the zoom is changed
        GEvent.addListener(getMap(), 'zoomend', function () {
            resetInfowindowPane();
        });

        GEvent.addListener(getMap(), 'click', function (marker) {

            // clicked on a marker, which excludes cluster markers
            if (marker && (marker instanceof GMarker)) {
                if (marker.medelaInfowinHtml !== null) {
                    /**
                     * TODO: Optional, but to improve performance for a second click on the same marker,
                     * the html could be cached.
                     */
                }
                
                getLocationData(marker);
            } else {

                return true;
            }
        });

   

        // make sure the cluster manager refreshes properly when zooming
        GEvent.addListener(getMap(), 'zoomend', function () {
            if (markerClstrr) {
                markerClstrr.resetViewport();
            }
        });

        MEDELA.locationfinder.handleEvent('searchDataReceived', function () {
            getMap().clearOverlays();

            setMapInititalPosition(getMap());

            clearMarkers();

            setDataObject(MEDELA.locationfinder.search.form.getDataObj());

            createClusteredMarkers(getMap(), MEDELA.locationfinder.search.form.getLocationData(), true);

        });
    };

    return {
        'init' : init,
        'getLocationData' : getLocationData,
        'getMap' : getMap,
        'MARKER_MAX_ZOOM' : MARKER_MAX_ZOOM
    };

}());

jQuery(document).ready(function () {
    MEDELA.locationfinder.gmap.map.init();
});
