/* gpsdings public javascript API
 * http://gpstools.sourceforge.net/js/GPSDings-###.js
 * Copyright (C) 2008 Moritz Ringler
 * $Id: GPSDings-0.6-1.js 432 2009-10-25 11:15:22Z ringler $
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//GPSDings namespace
if (GPSDings == null || typeof(GPSDings) != "object") { var GPSDings = new Object();}

//GPSDings.OpenLayersMap class
GPSDings.OpenLayersMap = function(divid, options) {

    /* PRIVATE FIELDS */
    var map;
    /** Vector layers */
    var markers;
    var tracks;
    var photos;
    /** The map's projection */
    var epsg4326 = new OpenLayers.Projection("EPSG:4326");
    /** the number of zoomlevels of the map */
    var nz= 1;

    var sf;

    var defaulticon = new OpenLayers.Icon('http://gpstools.sf.net/js/markers/marker.png',
        new OpenLayers.Size(20, 34),
        null,
        function(size) {
            return new OpenLayers.Pixel(-10, -34);
        });


    var style_mark = {
        graphicWidth: 20,
        graphicHeight: 34,
        graphicXOffset: -10,
        graphicYOffset: -34,
        externalGraphic: 'http://gpstools.sf.net/js/markers/marker.png'
    };

    var style_photo = {
        graphicWdith: 12,
        graphicHeight: 9,
        graphicXOffset: -6,
        graphicYOffset: -4,
        externalGraphic: 'http://gpstools.sourceforge.net/js/camera.png'
    };

    /* PRIVATE METHODS */
    /** Creates the map's base layers */
    function createBaseLayers(baseLayer){
        var blayers = [];
        for (var layer in baseLayer){
            if(!baseLayer[layer]){
                continue;
            }
            switch (layer){
            case "gmap":
                blayers.push(new OpenLayers.Layer.Google(
                    "Google Map" , {type: G_NORMAL_MAP,
                    sphericalMercator: true }));
                break;
            case "gsat":
                blayers.push(new OpenLayers.Layer.Google(
                    "Google Satellite" , {type: G_SATELLITE_MAP,
                    sphericalMercator: true }));
                break;
            case "ghyb":
                blayers.push(new OpenLayers.Layer.Google(
                    "Google Hybrid" , {type: G_HYBRID_MAP,
                    sphericalMercator: true }));
                break;
            case "gterrain":
                blayers.push(new OpenLayers.Layer.Google(
                    "Google Terrain" , {type: G_PHYSICAL_MAP,
                    sphericalMercator: true }));
                break;
            case "msvemap":
                blayers.push(new OpenLayers.Layer.VirtualEarth( "MS Virtual Earth Map (buggy)", {
                        type: VEMapStyle.Road,
                        sphericalMercator: true
                }));
                break;
            case "msvesat":
                blayers.push(new OpenLayers.Layer.VirtualEarth( "MS Virtual Earth Satellite (buggy)", {
                        type: VEMapStyle.Aerial,
                        sphericalMercator: true
                }));
                break;
            case "msvehyb":
                blayers.push(new OpenLayers.Layer.VirtualEarth( "MS Virtual Earth Hybrid (buggy)", {
                        type: VEMapStyle.Hybrid,
                        sphericalMercator: true
                }));
                break;
            case "ymap":
                blayers.push(new OpenLayers.Layer.Yahoo( "Yahoo Map", {
                        type: YAHOO_MAP_REG,
                        sphericalMercator: true
                }));
                break;
            case "ysat":
                blayers.push(new OpenLayers.Layer.Yahoo( "Yahoo Satellite", {
                        type: YAHOO_MAP_SAT,
                        sphericalMercator: true
                }));
                break;
            case "yhyb":
                blayers.push(new OpenLayers.Layer.Yahoo( "Yahoo Hybrid", {
                        type: YAHOO_MAP_HYB,
                        sphericalMercator: true
                }));
                break;
            case "osmmapnik":
                blayers.push(new OpenLayers.Layer.OSM.Mapnik("OpenStreetMap (Mapnik)", {
                        displayOutsideMaxExtent: true,
                        wrapDateLine: true
                }));
                break;
            case "osmosmarender":
                blayers.push(new OpenLayers.Layer.OSM.Osmarender("OpenStreetMap (Osmarender)", {
                        displayOutsideMaxExtent: true,
                        wrapDateLine: true
                }));
                break;
            case "osmcyclemap":
                blayers.push(new OpenLayers.Layer.OSM.CycleMap("OpenStreetMap (Cycle Map)", {
                        displayOutsideMaxExtent: true,
                        wrapDateLine: true
                }));
                break;
            default:
                alert("Unknown layer " + layer);
            }
        }
        map.addLayers(blayers);
    for(var i = 0; i<blayers.length; i++){
            nz = Math.max(nz, blayers[i].numZoomLevels);
    }
        return map;
    }

    /** Coordinate transformation: user to map */
    function toMap(position){
        return position.clone().transform(epsg4326, map.getProjectionObject());
    }

    /** Coordinate transformation: map to user */
    function fromMap(position){
        return position.clone().transform(map.getProjectionObject(), epsg4326);
    }

    function createVectorLayer(name){
        var vL;
        // we want opaque external graphics and non-opaque internal graphics
        var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
        layer_style.fillOpacity = 0.2;
        layer_style.graphicOpacity = 1;

        vL = new OpenLayers.Layer.Vector(name, {
                style: layer_style,
                units: "m",
                numZoomLevels: nz
        });

        vL.popup = null;

        vL.closePopup = function() {
            if (vL.popup != null) {
                map.removePopup(vL.popup);
                vL.popup =  null;
            }
        }
        vL.openPopup = function(feature, mouseXY) {
            vL.closePopup();
            vL.popup = new OpenLayers.Popup.FramedCloud("popup",
                map.getLonLatFromViewPortPx(mouseXY),
                null,
                feature.attributes.name, null, false);
            vL.popup.setBackgroundColor("#FFFFFF");
            if (feature.attributes.desc){
                vL.popup.contentHTML = feature.attributes.desc;
            } else {
                vL.popup.contentHTML = '<span style="font-family:sans-serif; color:' + feature.attributes.color + '">' + feature.attributes.name + '</span>';
            }
            map.addPopup(vL.popup);
            vL.popup.show();
        }
        return vL;
    }

    function makeSelectFeature(){
        if (sf){
            sf.deactivate();
            map.removeControl(sf);
        }

        //SelectFeature does not work (yet?) with multiple layers
        var vL = [];
        if (photos){
            vL.push(photos);
        }
        if (markers){
            vL.push(markers);
        }
        if(tracks){
            vL.push(tracks);
        }
        /*
        //var vL = [];
        if(tracks){
            vL = tracks;
        }
        if (photos){
            vL = photos;
        }
        if (markers){
            vL = markers;
        }*/

        /* add SelectFeature that displays track (segment) names */
        sf = new OpenLayers.Control.SelectFeature(vL,
            {
                hover: true,
                highlightOnly: true,
                renderIntent: "temporary",
                eventListeners: {
                    beforefeaturehighlighted: null,
                    featurehighlighted: function(args){
                        var feature = args.feature;
                        var selectFeature = args.object;
                        if (feature.attributes){
                            window.status = feature.attributes.name;
                            feature.layer.openPopup(feature, selectFeature.handlers.feature.evt.xy);
                        }
                    },
                    featureunhighlighted: function(args){
                        var feature = args.feature;
                        if (feature.attributes){
                            window.status = '';
                            feature.layer.closePopup();
                        }
                    }
                }
            });

        map.addControl(sf);
        sf.activate();
        return sf;
    }


    /* PRIVILEGED METHODS */
    /** Adds a marker to the map. */
    this.addWpt = function(wpt) {
        /* Lazily create and add marker layer. */
        if(markers == null){
            markers = createVectorLayer("Waypoints");
            map.addLayer(markers);
            makeSelectFeature();
        }

        /* create marker and add it to markers layer */
        var attrib = {
            location: (new OpenLayers.LonLat(wpt.lon,wpt.lat)).transform(epsg4326, map.getProjectionObject())
        };
        if (wpt.img) {
            attrib.desc = '<img src="' + wpt.img.src +
                        + '" width="' + wpt.img.width
                        + '" height="' + wpt.img.height
                        + '" alt="' + wpt.desc +'" />';
        } else if (wpt.desc) {
            attrib.desc = wpt.desc;
        } else {
            attrib.name = wpt.name;
            attrib.color= "black";
        }
        var marker = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(attrib.location.lon, attrib.location.lat),
            attrib, style_mark);
        markers.addFeatures([ marker ]);

        return marker;
    };

    /** Adds a photo marker to the map. */
    this.addPhotoWpt = function(wpt) {

        /* Lazily create and add photos layer. */
        if(photos == null){
            photos = createVectorLayer("Photos");
            map.addLayer(photos);
            makeSelectFeature();
        }

        /* create marker and add it to photo layer */
        var attrib = {
            location: (new OpenLayers.LonLat(wpt.lon,wpt.lat)).transform(epsg4326, map.getProjectionObject())
        };

        if (wpt.img) {
            attrib.desc = '<img src="' + wpt.img.src
                        + '" width="' + wpt.img.width
                        + '" height="' + wpt.img.height
                        + '" alt="' + wpt.desc +'" />';
        } else if (wpt.desc) {
            attrib.desc = wpt.desc;
        } else {
            attrib.name = wpt.name;
            attrib.color= "black";
        }
        var marker = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(attrib.location.lon, attrib.location.lat),
            attrib, style_photo);
        photos.addFeatures([ marker ]);
        return marker;
    };

    /** Removes a marker from the map. */
    this.removeMarkerFromMap = function(marker){
        markers.removeMarker(marker);
    };

    /**  Adds a polyline to the tracks layer.*/
    this.addTrkSeg = function(trkseg, segname){

        /* Lazily create and add trk layer. */
        if(tracks == null){
            tracks = createVectorLayer("Tracks");
            map.addLayer(tracks);
            makeSelectFeature();
        }
        var style = {
            strokeColor: trkseg.color,
            strokeWidth: 3,
            pointRadius: 6,
            pointerEvents: "visiblePainted"
        };
        var mapproj =  map.getProjectionObject();
        var points = [];
        for(var i=0; i< trkseg.trkpt.length; i++){
            points.push((new OpenLayers.Geometry.Point(trkseg.trkpt[i].lon, trkseg.trkpt[i].lat)).transform(epsg4326, mapproj));
        }
        var attrib = (segname)? {
            name: segname,
            location: (new OpenLayers.LonLat(trkseg.trkpt[0].lon, trkseg.trkpt[0].lat)).transform(epsg4326, mapproj),
            color: trkseg.color
        } : null;
        tracks.addFeatures([new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.LineString(points), attrib, style)]);
    };

    /** Adds a kml layer */
    this.addKML = function(title, url){
        map.addLayer(new OpenLayers.Layer.GML(title, url,
            {projection: epsg4326, format: OpenLayers.Format.KML, formatOptions:
            {extractStyles: true, extractAttributes: true}}));
    };

    /*
    Wrappers for map methods.
    */
    this.getCenter = function() {
        return fromMap(map.getCenter());
    };

    this.setCenter = function(center, zoom) {
        map.setCenter(toMap(center.clone()));
    };

    this.setExtent = function(extent) {
        map.zoomToExtent(toMap(extent));
    };

    this.getExtent = function() {
        return fromMap(map.getExtent());
    };

    this.getEventPosition = function(event) {
        return fromMap(map.getLonLatFromViewPortPx(event.xy));
    };

    this.getSize = function(){
        return map.getSize();
    };

    this.updateSize = function(){
        map.updateSize();
    };

    /* constructor proper */
    options = options || {};
    var args = {
        controls: options.controls || [
        new OpenLayers.Control.ArgParser(),
        new OpenLayers.Control.Attribution(),
        new OpenLayers.Control.LayerSwitcher(),
        new OpenLayers.Control.Navigation(),
        new OpenLayers.Control.PanZoomBar(),
        new OpenLayers.Control.ScaleLine()
        ],

        projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: epsg4326,
        units: "m",
        maxResolution: 156543.0339,
        maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
            20037508, 20037508.34)
    };
    map =  new OpenLayers.Map(divid, args);
    createBaseLayers(options.baselayers || { gmap: true } );
}

GPSDings.OpenLayersMap.prototype.setBounds = function(bb) {
    var bounds = new OpenLayers.Bounds();
    bounds.extend(new OpenLayers.LonLat(bb.W, bb.S));
    bounds.extend(new OpenLayers.LonLat(bb.E, bb.N));
    this.setExtent(bounds);
};

/* CLASS PICTURES */
GPSDings.Pictures = function(photowpts){
    this.photowpts = photowpts;

    //Adds the PhotoWpts as markers to a GoogleMap GMap2 object
    this.addToGMap2 = function (gmap,  imageurlPrefix, maxWidth, icon){
       // Creates a marker at the given point with the given photograph
       function
       createPictureMarker(p){//point, options, pic, width, height) {
           var options = {clickable: true, title: p.desc};
           if(icon){
               options.icon = icon;
           }
           var marker = new GMarker(new GLatLng(p.lat, p.lon), options);
           var imageLoc = imageurlPrefix + p.img.src;
           var scaling = 1;
           if (p.img.width > maxWidth){
               scaling = 1.0 * maxWidth/p.img.width;
           }
           GEvent.addListener(marker, 'click', function() {
               marker.openInfoWindowHtml(
                    '<img src="' + imageLoc + '"' +
                        ' alt="' + imageLoc + '\"' +
                        ' width=\"' + Math.round(scaling * p.img.width) + '\"' +
                        ' height=\"' + Math.round(scaling * p.img.height) + '\" />');
           });
           return marker;
       }

       for (var i=0; i<this.photowpts.length; i++){
           gmap.addOverlay(createPictureMarker(photowpts[i]));
       }
    };

    this.addToOpenLayersMap = function (omap, imageurlPrefix, maxWidth, icon){
        var myIcon = icon;
        if(!myIcon){
            var url = 'http://gpstools.sourceforge.net/js/camera.png';
            var sz = new OpenLayers.Size(12, 9);
            var calculateOffset = function(size) {
                return new OpenLayers.Pixel(-(size.w/2), -(size.h/2));
            };
            myIcon = new OpenLayers.Icon(url, sz, null, calculateOffset);
        }
        for (var i=0; i<this.photowpts.length; i++){
           var p = photowpts[i];
           var scaling = 1.0;
           if (p.img.width > maxWidth){
               scaling = 1.0 * maxWidth/p.img.width;
           }
           var crs = null;
           if(imageurlPrefix != null && imageurlPrefix != ""){
               crs = imageurlPrefix + p.img.src;
           }
           if(scaling != 1.0 || crs || !p.icon){
               var p2 = {
                   desc: p.desc,
                   lat: p.lat,
                   lon: p.lon,
                   img: {
                       src: ((crs)? crs : p.img.src),
                       width: p.img.width * scaling,
                       height: p.img.height * scaling
                   },
                   icon: myIcon.clone()
               }
               p = p2;
           }
           omap.addPhotoWpt(p);
       }
    };
};

