/**
 * Create a Google, bing or klickTel Map.
 *
 * The Widget inserts a Google, bing or klickTel Map into the target element on which the widget is executed on.
 *
 * ### Examples
 * The Widget is executed on a pageelement and is called with passing the chosen options. In this example a Google Map is implemented. Zoom factor is 5, controls are shown and the map area is not draggable.
 *
 * JavaScript:
 *
 *
 *     jQuery.ready({
 *          plugin: [ 'de_epages.externalcontent.ui.maps' ],
 *          DOM:    true
 *      }, function($){
 *          var widget = de_epages('<div>').externalcontentUiMaps({
 *              type: "google",
 *              width: 300,
 *              height: 200,
 *              zoom: 5,
 *              title: "Milestones GmbH",
 *              controls: true,
 *              fixedView: true,
 *              lat: 50.65531,
 *              lng: 11.715550000000007
 *          });
 *     });
 *
 *
 * @class jQuery.ui.externalcontentUiMaps
 * @extends jQuery.widget
 *
 * @uses de_epages
 * @uses jQuery.ui.widget
 * @uses jQuery.tmpl
 * @uses jQuery.uid
 * @uses de_epages.externalcontent.ui.rss
 * @since 6.15.0
 */

/**
 * This method removes the container on which the map gadget has been appended to.
 *
 * @method destroy
 * @member jQuery.ui.externalcontentUiMaps
 *
 * @since 6.15.0
 */

/**
 * @cfg {String} [type] Specifies from which provider the map should be shown. Values: 'google', 'bing', 'klicktel'
 */

/**
 * @cfg {Integer} [width] Defines the width of the map in pixels.
 */

/**
 * @cfg {Integer} [height] Defines the height of the map in pixels.
 */

/**
 * @cfg {Integer} [zoom] Defines the zoom factor of the shown place on the map.
 */

/**
 * @cfg {String} [title] Defines the title of the map widget, e.g. the name and adress of the company.
 */

/**
 * @cfg {Boolean} [controls] Specifies whether to display the control elements of the embedded map.
 */

/**
 * @cfg {Boolean} [fixedView] Prevents the map from being dragged, if false.
 */

/**
 * @cfg {Float} [lat] Specifies the latitude (geographic coordinate) of the shown place on the map.
 */

/**
 * @cfg {Float} [lng] Specifies the longitude (geographic coordinate) of the shown place on the map.
 */

/**
 * See `jQuery.ui.externalcontentUiMaps` for details.
 *
 * @param {Object} [options] A map of additional options to pass to the method.
 * @param {String} [type] Specifies from which provider the map should be shown. Values: 'google', 'bing', 'klicktel'
 * @param {Integer} [width] Defines the width of the map in pixels.
 * @param {Integer} [height] Defines the height of the map in pixels.
 * @param {Integer} [zoom] Defines the zoom factor of the shown place on the map.
 * @param {String} [title] Defines the title of the map widget, e.g. the name and adress of the company.
 * @param {Boolean} [controls] Specifies whether to display the control elements of the embedded map.
 * @param {Boolean} [fixedView] Prevents the map from being dragged, if false.
 * @param {Float} [lat] Specifies the latitude (geographic coordinate) of the shown place on the map.
 * @param {Float} [lng] Specifies the longitude (geographic coordinate) of the shown place on the map.
 *
 * @method externalcontentUiMaps
 * @member jQuery
 *
 * @since 6.15.0
 */

/*
 * @copyright   © Copyright 2006-2012, epages GmbH, All Rights Reserved.
 *
 * @module      de_epages.externalcontent.ui.maps
 *
 * @revision    $Revision: 1.12 $
 */
/*jslint nomen: true, browser: true*/
/*global define*/
define('de_epages/externalcontent/ui/maps', [
    'jquery/ui/widget',
    'ep',
    'de_epages',
    '$dict!../dictionary',

    'util/browser',
    'jquery/tmpl',
    'jquery/uid'
], function ($, ep, de_epages, dict) {
    'use strict';
    // Gagdet class name
    var gadgetClassName = '',

        // Container template id
        tmplContainerId = 'de_epages.externalcontent.ui.maps',

        // Cache of ajax deferreds by url
        ajaxCache = {};

    // Render and cache template.
    $.template(tmplContainerId, '<div style="width: ${width}px; height: ${height}px;{{if type === "bing"}} position: relative;{{/if}}"></div>');

    // Define widget
    $.widget('ui.externalcontentUiMaps', {
        options: {
            type: 'google',
            controls: true,
            fixedView: false,
            width: 500,
            height: 300,
            lat: 50.9287910,
            lng: 11.5846220,
            zoom: 15,
            title: ''
        },

        _create: function () {
            var self = this,
                type = self.options.type,
                api = self.api = self[type] && self[type].render ? self[type] : self.google; // Use google as default fallback
            // Load API async
            ((api.load && api.load.call(self)) || $.Deferred().resolve()).done(function (data) {
                // Add ui class name
                self.element.addClass(gadgetClassName);

                // Start rendering
                api.render.call(self, data);
            });
        },

        // Google Map load and render API
        google: {
            // Load API and return deferred object or false
            load: function () {
                var url = 'https://www.google.com/jsapi';

                // API already loaded
                if (window.google && window.google.load) {
                    return;
                }

                // Request already started
                if (ajaxCache[url]) {
                    return ajaxCache[url];
                }

                // Request API
                return (ajaxCache[url] = ep.ajax({
                    url: url,
                    dataType: 'script',
                    cache: true
                }));
            },
            // Render map using loaded api
            render: function () {
                var self = this,
                    o = self.options;

                // Load Google Maps API.
                window.google.load('maps', '3', {
                    other_params: 'sensor=false',
                    callback: function () {
                        var centerMarker, center;

                        // Init *container*.
                        self.container = $.tmpl(tmplContainerId, o).appendTo(self.element.empty());

                        // Init *map*.
                        centerMarker = new window.google.maps.LatLng(o.lat, o.lng);
                        center = o.center ? new window.google.maps.LatLng(o.center.lat, o.center.lng) : centerMarker;

                        self.marker = new window.google.maps.Marker({
                            position: centerMarker
                        });

                        if (o.title) {
                            self.marker.setTitle(o.title);
                        }

                        self.infoWindow = new window.google.maps.InfoWindow({
                            content: self.getDescription('https://maps.google.com/maps?saddr=${lat},${lng}', 'https://maps.google.com/maps?daddr=${lat},${lng}'),
                            position: center
                        });
                        self.infoWindow.isOpen = false;

                        self.map = new window.google.maps.Map(self.container[0], {
                            disableDefaultUI: !o.controls,
                            draggable: !o.fixedView,
                            center: center,
                            mapTypeId: o.mapType || 'roadmap',
                            zoom: o.zoom
                        });

                        self.marker.setMap(self.map);

                        window.google.maps.event.addListener(self.marker, 'click', function () {
                            // a click on the marker toggles the info window: if it is open, it
                            // should be closed and if it is closed it should be opened.

//#JSCOVERAGE_IF false
//because of faked request no marker is testable
                            if (self.infoWindow.isOpen) {
                                self.infoWindow.close();
                            } else {
                                self.infoWindow.open(self.map, this);
                            }

                            self.infoWindow.isOpen = !self.infoWindow.isOpen;
//#JSCOVERAGE_ENDIF
                        });

                        window.google.maps.event.addListener(self.map, 'click', function () {
                            // a click on the map closes the info window.
//#JSCOVERAGE_IF false
//because of faked request no clicks can be tested
                            self.infoWindow.close();
                            self.infoWindow.isOpen = false;
//#JSCOVERAGE_ENDIF
                        });

                        if (typeof o.callback === 'function') {
                            o.callback(o.type, self.map, self.marker);
                        }
                    }
                });
            }
        },

        // Bing Map load and render API
        bing: {
            // Load API and return deferred object or false
            load: function () {
                var url = '//ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2' + (ep.config.requestProtocolAndServer ? '&s=1' : '');

                // API already loaded
                if (window.VEMap) {
                    return;
                }

//#JSCOVERAGE_IF false
// We cannot come to this code because we need a window.VEMap for testing
                // Request already started
                if (ajaxCache[url]) {
                    return ajaxCache[url];
                }
                // Request API
                return (ajaxCache[url] = ep.ajax({
                    url: url,
                    dataType: 'script',
                    cache: true
                }));
//#JSCOVERAGE_ENDIF
            },
            // Render map using loaded api
            render: function () {
                var self = this,
                    o = self.options,
                    uid = $.uid(),
                    map,
                    center,
                    interval,
                    initBingMap = function () {
                        map.LoadMap(center, o.zoom, o.mapType || window.VEMapStyle.Road, o.fixedView ? true : false, o.mode ? window.VEMapMode[o.mode] : window.VEMapMode.Mode2D);

                        if (!o.controls) {
                            map.HideDashboard();
                        }

                        self.marker = map.AddPushpin(center);

                        if (o.title) {
                            self.marker.SetDescription(self.getDescription('http://www.bing.com/maps/?rtp=pos.${lat}_${lng}', 'http://www.bing.com/maps/?rtp=~pos.${lat}_${lng}'));
                        }

                        if (typeof o.callback === 'function') {
                            o.callback(o.type, self.map, self.marker);
                        }

                        map.ShowInfoBox(self.marker);
                    };
                // Init *container*.
                self.container = $.tmpl(tmplContainerId, o).attr('id', uid).appendTo(self.element.empty());
                // Init *map*.
                map = self.map = new window.VEMap(uid);
                center = new window.VELatLong(o.lat, o.lng);
                if ($.browser.mozilla) {
//#JSCOVERAGE_IF false
                    // If firefox is detected, use a setInterval to check that the window.VEMap exists and add an additional check to wait until attachEvent exists.
                    interval = setInterval(function () {
                        if (typeof window.VEMap !== "undefined" && document.getElementById(uid).attachEvent !== undefined) {
                            clearInterval(interval);
                            initBingMap();
                        }
                    }, 10);
//#JSCOVERAGE_ENDIF
                } else {
                    initBingMap();
                }
            },
            // Destroy rendered map
            destroy: function () {
                this.map.Dispose();
            }
        },

        // KlickTel Map load and render API
        klicktel: {
            // Load API and return deferred object or false
            load: function () {
                var klicktel = 'de_epages.externalcontent.ui.klicktel';

                if (!$.template[klicktel]) {
                    $.template(klicktel, '<iframe src="?' + 'ViewAction=ViewKlicktelMap&ChangeAction=CalculateKlicktelMap' + '&ObjectID=${ep.config.siteId}&Address=${address}&MapWidth={{if autoWidth}}auto{{else}}${width}{{/if}}&MapHeight=${height}' + '{{if contactPerson}}&ContactPerson=${contactPerson}{{/if}}{{if email}}&EMail=${email}{{/if}}&UseRoute={{if useRoute}}1{{else}}0{{/if}}' + '&LargeNavigation={{if largeNavigation}}1{{else}}0{{/if}}' + '&ShowAddress={{if showAddress}}1{{else}}0{{/if}}&LayoutLR={{if layoutLR}}1{{else}}0{{/if}}" ' + 'width=${width} height=${height} frameborder=0></iframe>');
                }

                return;
            },
            // Render map using loaded api
            render: function () {
                var self = this;

                self.container = $.tmpl('de_epages.externalcontent.ui.klicktel', self.options).appendTo(self.element.empty());
            }
        },

        destroy: function () {
            // Remove all traces of the widget.
            var self = this,
                o = self.options;

            // Remove gadget class name
            self.element.removeClass(gadgetClassName);

            // Call destroy of map type
            if (self.api.destroy) {
                self.api.destroy.call(self);
            }

            // Remove container
            if (self.container) {
                self.container.remove();
            }

            self._superApply(arguments);
        },

        getDescription: function (fromUrl, toUrl) {
            return this.options.title.replace(/,/g, '<br>') + '<br /> ' + dict.translate('Route') + ': <a href="' + $.tmpl(fromUrl, this.options).text() + '" target="_blank">' + dict.translate('FromHere') + '</a> | <a href="' + $.tmpl(toUrl, this.options).text() + '" target="_blank">' + dict.translate('ToHere') + '</a>';
        }

    });

    return de_epages;

});