Ext.namespace('cmv.Gmap');

cmv.Gmap.Container = Ext.extend(Ext.Container, {
  gmapType : 'map',
  zoomLevel: 14,
  setCenter: {
    lat: 56.46872,
    lng: 9.40566
  },
  sponsorrace:false,
  // private
  mapDefined: false,
  // private
  mapDefinedGMap: false,
  firstLeg:true,
  initComponent : function(){

    this.addEvents(
      /**
         * @event mapready
         * Fires when the map is ready for interaction
         * @param {GMapPanel} this
         * @param {GMap} map
         */
      'mapready',
      /**
         * @event apiready
         * Fires when the Google Maps API is loaded
         */
      'apiready'
      );

    Ext.applyIf(this,{
      markers: [],
      waypoints :[],
      elevationpath:[],
      routelength:0,
      cache: {
        marker: [],
        polyline:[],
        infowindow: []
      }
    });

    cmv.Gmap.Container.superclass.initComponent.call(this);
    
    if(Ext.getDom('googlemapsscript')){
      this.on('afterrender', this.apiReady, this);
    }else{
      window.gmapapiready = this.apiReady.createDelegate(this);
      this.buildScriptTag('http://maps.google.com/maps/api/js?sensor=false&callback=gmapapiready');
    }
  },
  apiReady : function(){

    if (this.rendered){

      (function(){

        this.gmap = new google.maps.Map(this.el.dom, {
          zoom:this.zoomLevel,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });
        this.mapDefined = true;
        this.mapDefinedGMap = true;

        google.maps.event.addListenerOnce(this.getMap(), 'tilesloaded', this.onMapReady.createDelegate(this));
        google.maps.event.addListener(this.getMap(), 'dragend', this.dragEnd.createDelegate(this));
    
        if (typeof this.setCenter === 'object') {
          if (this.gmapType === 'map'){
            var point = new google.maps.LatLng(this.setCenter.lat,this.setCenter.lng);
            this.getMap().setCenter(point, this.zoomLevel);
            this.lastCenter = point;
          }
          if (typeof this.setCenter.marker === 'object' && typeof point === 'object') {
            this.addMarker(point, this.setCenter.marker, this.setCenter.marker.clear);
          }
        }

      }).defer(200,this);

    }else{
      this.on('afterrender', this.apiReady, this);
    }
  },
  // private
  afterRender : function(){

    var wh = this.ownerCt.getSize();
    Ext.applyIf(this, wh);

    cmv.Gmap.Container.superclass.afterRender.call(this);

  },
  // private
  buildScriptTag: function(filename, callback) {
    var script  = document.createElement('script'),
    head        = document.getElementsByTagName("head")[0];
    script.type = "text/javascript";
    script.src  = filename;
    script.id   = "googlemapsscript";

    return head.appendChild(script);
  },
  // private
  onMapReady : function(){
    //this.addMapControls();
    //this.addOptions();
    this.addDirection(this.waypoints);
    this.addMarkers(this.markers);
    this.fireEvent('mapready', this, this.getMap());

  },
  // private
  onResize : function(w, h){

    cmv.Gmap.Container.superclass.onResize.call(this, w, h);

    // check for the existance of the google map in case the onResize fires too early
    if (typeof this.getMap() == 'object') {
      google.maps.event.trigger(this.getMap(), 'resize');
      if (this.lastCenter){
        this.getMap().setCenter(this.lastCenter, this.zoomLevel);
      }
    }

  },
  // private
  setSize : function(width, height, animate){

    cmv.Gmap.Container.superclass.setSize.call(this, width, height, animate);

    // check for the existance of the google map in case setSize is called too early
    if (Ext.isObject(this.getMap())) {
      google.maps.event.trigger(this.getMap(), 'resize');
      if (this.lastCenter){
        this.getMap().setCenter(this.lastCenter, this.zoomLevel);
      }
    }

  },
  // private
  dragEnd: function(){
    this.lastCenter = this.getMap().getCenter();
  },
  /**
     * Returns the current google map which can be used to call Google Maps API specific handlers.
     * @return {GMap} this
     */
  getMap : function(){

    return this.gmap;

  },
  /**
     * Returns the maps center as a GLatLng object
     * @return {GLatLng} this
     */
  getCenter : function(){

    return this.getMap().getCenter();

  },
  /**
     * Returns the maps center as a simple object
     * @return {Object} this has lat and lng properties only
     */
  getCenterLatLng : function(){

    var ll = this.getCenter();
    return {
      lat: ll.lat(),
      lng: ll.lng()
    };

  },
  /**
     * Creates a polyline from a direction request.
     * @param {Array} waypoints an array of {lat:number,lng:number} objects include start and end points
     */
  addDirection : function(waypoints) {
    if (Ext.isArray(waypoints)){
      if(waypoints.length>1){
        directionsDisplay = new google.maps.DirectionsRenderer();
        this.cache.polyline=new google.maps.Polyline({
          strokeColor:'#3AAA5E',
          strokeOpacity:0.75
        });
        this.cache.polyline.setMap(this.getMap());
        var directionsService = new google.maps.DirectionsService();
        var wp=new Array(), dreq=new Array();
        for (i = 0; i < waypoints.length; i++) {
          if (waypoints[i]) {
            wp.push({
              location:new google.maps.LatLng(waypoints[i].lat,waypoints[i].lng),
              stopover:false
            });
          }
        }
        var j=0;
        for(var i=0;i<wp.length-1;)
        {
          j=(j+9>wp.length-1?wp.length-1:j+9);
          dreq.push ({
            origin: wp[i].location,
            destination: wp[j].location,
            waypoints:wp.slice(i+1,j),
            travelMode: google.maps.DirectionsTravelMode.WALKING,
            unitSystem: google.maps.DirectionsUnitSystem.METRIC,
            optimizeWaypoints: false,
            provideRouteAlternatives: true,
            avoidHighways: true,
            avoidTolls: true,
            region: 'dk'
          })
          i=j;
        }
        window.gmapdirectionready = this.directionReady.createDelegate(this);
        for(i=0;i<dreq.length;i++)
          directionsService.route(dreq[i],gmapdirectionready)
        this.markers.push({
          lat:waypoints[0].lat,
          lng:waypoints[0].lng,
          marker:{
            title:'Start',
            icon: 'images/cyclingsport.png',
            shadow: 'images/shadow.png'
          }
        });
        this.markers.push({
          lat:waypoints[waypoints.length-1].lat,
          lng:waypoints[waypoints.length-1].lng,
          marker:{
            title:'Mål',
            icon: 'images/racing.png',
            shadow: 'images/shadow.png'
          }
        });
        if(this.sponsorrace)
        {          
          this.markers.push({
            lat:56.46872,
            lng:9.40566,
            marker:{
              title:'Pølser efter løbet i klubben',
              icon: 'images/cyclingfeedarea.png',
              shadow: 'images/shadow.png'
            }
          });          
        }
      }
    }

  },
  // private
  directionReady: function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      if(this.firstLeg)
      {
        bounds=(result.routes[0].bounds);
        this.firstLeg=false;
      }
      else
        bounds = bounds.union(result.routes[0].bounds);
      path=this.cache.polyline.getPath().getArray();
      var p=result.routes[0].legs[0];
      this.routelength+=p.distance.value;
      for(k=0;k<p.steps.length;k++)
        path=path.concat(p.steps[k].path);
      this.elevationpath=this.elevationpath.concat(result.routes[0].overview_path);
      this.cache.polyline.setPath(path);
      this.getMap().fitBounds(bounds);
      this.lastCenter = this.getMap().getCenter();
      this.getMap().setCenter(this.lastCenter, this.zoomLevel);
    }
    else
      Ext.Msg.alert('Status',status);
  },
  /**
     * Returns the direction path to be used in the google maps elevation service
     */
  getElevationPath : function(){
    return this.elevationpath;
  },
  /**
     * Creates markers from the array that is passed in. Each marker must consist of at least
     * <code>lat</code> and <code>lng</code> properties or a <code>geoCodeAddr</code>.
     * @param {Array} markers an array of marker objects
     */
  addMarkers : function(markers) {
    if (Ext.isArray(markers)){
      for (var i = 0; i < markers.length; i++) {
        if (markers[i]) {
          var mkr_point = new google.maps.LatLng(markers[i].lat, markers[i].lng);
          this.addMarker(mkr_point, markers[i].marker, false, markers[i].setCenter, markers[i].listeners);
        }
      }
    }
  },
  /**
     * Creates a single marker.
     * @param {Object} point a GLatLng point
     * @param {Object} marker a marker object consisting of at least lat and lng
     * @param {Boolean} clear clear other markers before creating this marker
     * @param {Boolean} center true to center the map on this marker
     * @param {Object} listeners a listeners config
     */
  addMarker : function(point, marker, clear, center, listeners){

    Ext.applyIf(marker,{});

    if (clear === true){
      this.clearMarkers();
    }
    if (center === true) {
      this.getMap().setCenter(point, this.zoomLevel)
      this.lastCenter = point;
    }

    var mark = new google.maps.Marker(Ext.apply(marker, {
      position: point
    }));

    if (marker.infoWindow){
      this.createInfoWindow(marker.infoWindow, point, mark);
    }

    this.cache.marker.push(mark);
    mark.setMap(this.getMap());

    if (typeof listeners === 'object'){
      for (evt in listeners) {
        google.maps.event.addListener(mark, evt, listeners[evt]);
      }
    }

    return mark;

  },
  // private
  clearMarkers : function(){

    this.hideAllInfoWindows();
    this.hideMarkers();

  },
  // private
  hideMarkers : function(){
    Ext.each(this.cache.marker, function(mrk){
      mrk.setMap(null);
    });
  },
  // private
  showMarkers : function(){
    Ext.each(this.cache.marker, function(mrk){
      mrk.setMap(this.getMap());
    },this);
  },
  /**
     * Creates an Info Window.
     * @param {Object} inwin an Info Window configuration
     * @param {GLatLng} point the point to show the Info Window at
     * @param {GMarker} marker a marker to attach the Info Window to
     */
  createInfoWindow : function(inwin, point, marker){

    var me = this, infoWindow = new google.maps.InfoWindow({
      content: inwin.content,
      position: point
    });

    if (marker) {
      google.maps.event.addListener(marker, 'click', function(){
        me.hideAllInfoWindows();
        infoWindow.open(me.getMap());
      });
    }

    this.cache.infowindow.push(infoWindow);

    return infoWindow;

  },
  // private
  hideAllInfoWindows : function(){
    for (var i = 0; i < this.cache.infowindow.length; i++) {
      this.cache.infowindow[i].close();
    }
  }
})

Ext.reg('cmvgmapcontainer',cmv.Gmap.Container);

cmv.Gmap.Profile = Ext.extend(Ext.Container, {
  layout:'fit',
  map:undefined,
  initComponent : function(){

    cmv.Gmap.Profile.superclass.initComponent.call(this);    
  },
  getElevation:function(){
    window.elevationready = this.addData.createDelegate(this);
    elservice = new google.maps.ElevationService();
    elservice.getElevationAlongPath({
      path:this.map.getElevationPath(),
      samples:400
    },elevationready);
  },
  // private
  addData:function(result,status){
    stepsize=this.map.routelength/399000;
    maxh=-10000;
    minh=-maxh;
    data = new Array();
    for(i=0;i<result.length;i++){
      hg=Math.round(result[i].elevation*100)/100;
      data.push(new Ext.data.Record({
        Dist:Math.round(i*stepsize*100)/100,
        Height:hg
      }));
      maxh=(maxh<hg)?hg:maxh;
      minh=(minh>hg)?hg:minh;
    }
    cmv.Gmap.store=new Ext.data.JsonStore({
      fields:['Dist','Height'],
      data: [],
      idIndex: 0
    });
    cmv.Gmap.store.add(data);
    this.add({
      xtype: 'linechart',
      store: cmv.Gmap.store,
      xField: 'Dist',
      yField: 'Height',
      seriesStyles:{
        color: '#3AAA5E',
        alpha:0.3
      },
      yAxis: new Ext.chart.NumericAxis({
        title: 'Højde(m)',
        minimum: Math.floor(minh),
        maximum: Math.ceil(maxh)
      }),
      xAxis: new Ext.chart.NumericAxis({
        title: 'Afstand(km)',
        minimum: 0,
        maximum: Math.ceil(this.map.routelength/1000)
      }),
      extraStyle: {
        xAxis: {
          showLabels: true,
          labelRotation: -90
        },
        animationEnabled: true
      }
    })
    this.doLayout();
  }
});
Ext.reg('cmvgmapprofile',cmv.Gmap.Container);

cmv.Gmap.Window = Ext.extend(Ext.Window, {
  width:900,
  height:700,
  y:50,
  ruteid:undefined,
  title:'Ruteinfo',
  modal:true,
  sponsorrace:false,
  initComponent : function(){
    cmv.Gmap.Window.superclass.initComponent.call(this);
    this.add({
      xtype:'tabpanel',
      activeTab:0,
      items:[{
        title:'Kort',
        layout:'fit'
      },

      {
        title:'Højdeprofil',
        layout:'fit',
        listeners:{
          render:{
            fn:function(panel){
              panel.get(0).getElevation();
            }
          }
        }
      }]

    })
    cmv.Gmap.rute=new Ext.data.JsonStore({
      autoDestroy: true,
      url: 'json/sponsorrute.php?act='+this.ruteid,
      root: 'data',
      totalProperty: 'total',
      fields: [{
        name:'aktivitetsid',
        type:'int'
      },{
        name:'sekvens',
        type:'int'
      }, {
        name:'koordinat'
      }],
      listeners:{
        load: {
          fn: function(store, record, operation){
            var waypoints=new Array();
            store.each(function(rec){
              var coords=rec.get('koordinat').split(',');
              waypoints.push({
                lat:coords[0],
                lng:coords[1]
              });
            })
            operation.scope.getItem(0).add(new cmv.Gmap.Container({
              height:600,
              width:800,
              waypoints:waypoints,
              sponsorrace:operation.sponsorrace
            }))
            operation.scope.getItem(1).add(new cmv.Gmap.Profile({
              height:600,
              width:800,
              map:operation.scope.getItem(0).get(0)
            }))
            operation.scope.doLayout();
          }
        }
      }
    })
    cmv.Gmap.rute.load({
      scope:this.get(0),
      sponsorrace:this.sponsorrace
    });
  }
});
Ext.reg('cmvgmapwindow',cmv.Gmap.Container);
