<!DOCTYPE html>
<html>

<head>
	<link data-require="leaflet@0.7.3" data-semver="0.7.3" rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />

	<style>
		html,
		body,
		#map {
			margin: 0px;
			width: 100%;
			height: 100%;
			padding: 0px;
		}

		.leaflet-control-container {
			display: none;
		}

		/** FLOATING SOCIAL BUTTON **/

		#btnExit.show {
			-webkit-transform: translateY(-125%);
			transform: translateY(-125%);
		}

		#btnUnits.show {
			-webkit-transform: translateY(-250%);
			transform: translateY(-250%);
		}

		#btnJobs.show {
			-webkit-transform: translateY(-375%);
			transform: translateY(-375%);
		}

		#floatMenu {
			z-index: 99999;
			position: fixed;
			display: flex;
			align-items: center;
			bottom: 10%;
			right: 5%;
			width: 56px;
			height: 56px;
			cursor: pointer;
		}


		#floatMenu .icon-bar+.icon-bar {
			/*margin-top: 4px;*/
		}

		.floatMenu-nav {
			z-index: 9999;
			position: fixed;
			bottom: 10.5%;
			right: 4.5%;
			width: 60px;
			display: flex;
			flex-direction: column;
			align-items: center;
			opacity: 0;
			cursor: pointer;
			-webkit-transition: all 0.3s ease-in;
			transition: all 0.3s ease-in;
		}

		.floatMenu-nav img {
			width: 60px;
		}

		.btnMap {
			z-index: 9999;
			position: fixed;
			bottom: 10.5%;
			right: 5.5%;
			width: 48px;
			height: 48px;
			display: flex;
			flex-direction: column;
			align-items: center;
			opacity: 0;
			cursor: pointer;
			-webkit-transition: all 0.3s ease-in;
			transition: all 0.3s ease-in;
		}

		.unit-icon {
			display: inline-block;
		}

		.unit-icon img {
			height: 45px;
			padding: 0 3px;
			display: inline-block;
		}

		#floatMenu.show {
			/*box-shadow: 7px 7px 10px 0px rgba(0, 0, 0, 0.48);*/
		}

		.floatMenu-nav.show {
			visibility: visible;
			opacity: 1;
		}

		.btnMap.show {
			visibility: visible;
			opacity: 1;
		}

		.units-btn-con {
			z-index: 9999;
			position: fixed;
			align-items: center;
			opacity: 0;
			display: block;
			cursor: pointer;
			-webkit-transition: all 0.3s ease-in;
			transition: all 0.3s ease-in;
			visibility: hidden;
		}

		.units-btn-con.show {
			visibility: visible;
			opacity: 1;
		}
	</style>
</head>

<body>

	<script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
	<script data-require="leaflet@0.7.3" data-semver="0.7.3" src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>

	<div id="map"></div>

	<div id="floatMenu" class="waves-effect waves-light">
		<div id="wrapper">
			<img src="http://mirac.kocgokhan.com/fabmenu.png" style="width: 60px; height: 60px; margin-top: 20px ">
		</div>
	</div>

	<div id="btnExit" class="floatMenu-nav">
		<img src="http://mirac.kocgokhan.com/fabmap.png" style="width: 60px; height: 60px;">
	</div>

	<div id="btnJobs" class="floatMenu-nav">
		<img src="http://mirac.kocgokhan.com/crosshair.png" style="width: 60px; height: 60px;">
	</div>

	<div id="btnUnits" class="floatMenu-nav">
		<img src="http://mirac.kocgokhan.com/fabcar.png" style="width: 60px; height: 60px;">
	</div>

	<div class="units-btn-con">
		<div class="unit-icon" data-target="online-car">
			<img src="https://res.cloudinary.com/dqueufbs7/image/upload/v1546943032/images/blue-car-on.png">
		</div>

		<div class="unit-icon" data-target="rolanti-car">
			<img src="https://res.cloudinary.com/dqueufbs7/image/upload/v1546943031/images/yellow-car-on.png">
		</div>

		<div class="unit-icon" data-target="red-car">
			<img src="https://res.cloudinary.com/dqueufbs7/image/upload/v1546943031/images/red-car-on.png">
		</div>

		<div class="unit-icon" data-target="deactive-car">
			<img src="https://res.cloudinary.com/dqueufbs7/image/upload/v1546943031/images/grey-car-on.png">
		</div>
	</div>



	<script>
		var baselayersConf = [
			{
				"name": "google",
				"key": "google",
				"tileUrlTmpl": "https://mts{s}.google.com/vt/hl=tr&x={x}&y={y}&z={z}",
				"isElliptical": false,
				"subdomains": "0123",
				"minZoom": 4.5,
				"maxZoom": 22
			}
		];

		function quadkey(zyx) {
			var result = '';
			for (var i = zyx.z; i > 0; i--) {
				var digit = 0;
				var mask = 1 << (i - 1);
				if ((zyx.x & mask) != 0) {
					digit++;
				}
				if ((zyx.y & mask) != 0) {
					digit++;
					digit++;
				}
				result += digit;
			}
			return result;
		}


		var istanbulAltuf = L.latLng(41.0247173, 29.0465252);
		var ankaraAltuf = L.latLng(40.4246677, 32.2901031);
		var units_first_load = true;

		var map = L.map('map', {
			center: istanbulAltuf,
			zoomControl: false,
			zoom: 10,
			editable: true
		});
		L.control.scale().addTo(map);

		var basemapsLayers = baselayersConf.map(function (conf) {
			return L.tileLayer(
				conf.tileUrlTmpl,
				L.Util.extend({ quadkey: quadkey }, conf)
			);
		});

		var layersCtrl = L.control.layers(null, null, { autoZIndex: false }).addTo(map);
		basemapsLayers.forEach(function (layer) {
			layersCtrl.addBaseLayer(layer, layer.options.name);
		});

		map.addLayer(basemapsLayers[0]);

		var deactiveCarIcon = L.icon({
			iconUrl: 'https://panel.mahrek.com.tr/assets/images/deactive.svg',
			className: 'deactive-car unit_icon'
		});
		var offlineCarIcon = L.icon({
			iconUrl: 'https://panel.mahrek.com.tr/assets/images/red-car.svg',
			className: 'red-car unit_icon'
		});
		var yellowCarIcon = L.icon({
			iconUrl: 'https://panel.mahrek.com.tr/assets/images/rolanti_1.svg',
			className: 'rolanti-car unit_icon'
		});
		
		var onlineCarIcon1 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/1.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon2 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/2.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon3 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/3.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon4 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/4.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon5 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/5.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon6 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/6.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon7 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/7.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon8 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/8.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon9 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/9.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon10 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/10.svg',
        className:'online-car'
    });
    var onlineCarIcon11 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/11.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon12 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/12.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon13 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/13.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon14 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/14.svg',
        className:'online-car'
    });
    var onlineCarIcon15 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/15.svg',
        className:'online-car unit_icon'
    });
    var onlineCarIcon16 = L.icon({
        iconUrl: 'https://panel.mahrek.com.tr/assets/images/carDirections/16.svg',
        className:'online-car unit_icon'
    });

		var globalBridge;

		function setupWebViewJavascriptBridge(callback) {
			if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
			if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
			window.WVJBCallbacks = [callback];
			var WVJBIframe = document.createElement('iframe');
			WVJBIframe.style.display = 'none';
			WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
			document.documentElement.appendChild(WVJBIframe);
			setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0)
		}

		setupWebViewJavascriptBridge(function (bridge) {

			globalBridge = bridge;

			var uniqueId = 1;

			bridge.registerHandler('iosFunctionHandler', function (data, responseCallback) {

				// IOS -> WEB

				console.log('get ios event', data);

				var responseData = { 'result': '1' };
				responseCallback(responseData);
			})

			document.body.appendChild(document.createElement('br'))
		})


		var unitMarkers = [];
		var token = '';
		var customer = '';
		var request = '';
		var units_first_load = false;

		function initializUnits(_token, _customer) {
			unitMarkers = [];
			units_first_load = false;

			request = 'https://panel.mahrek.com.tr/' + _token + '/location-last/map/list';
			if (_customer) {
				request = 'https://panel.mahrek.com.tr/' + _token + '/location-last/map/list?customerId=' + _customer;
			}


			fetch(request)
				.then(response => response.json())
				.then(data => {

					var unitslayerGroup = L.layerGroup();

					data.forEach(function (value, key) {
						if (value.unitTypeId === 1) {
							var markerItem = L.marker(
								[0, 0],
								{
									ignition: false,
									status: value['status'],
									unitTypeId: value['unitTypeId'],
									icon: deactiveCarIcon,
									unitId: value['unitId'],
									groupId: value['unitGroupId'],
									nickname: value['nickname'],
									driverName: value['driverName'],
									identifier: value['identifier'],
									markerType: "Unit"
								}

							).on('click', function (e) {

								//send to mirac ios

								marker_is_clicked = true;
								console.log(this.options.unitId)

								globalBridge.callHandler('iosCallback', { 'unitId': this.options.unitId }, function (response) {

									log('JS got response', response)
								});


							});

							unitslayerGroup.addLayer(markerItem);

							unitMarkers.push(markerItem);
						}

						unitslayerGroup.addTo(map);
						units_first_load = true;

					});

				}).catch(err => console.log(err));
		};

		initializUnits('47fa6e38-cf0b-4a7e-9bc6-d255c65829df', '55');

		function append_unit_id(icon, unit_id) {
			var classes = icon.options.className.split(' ');
			icon.options.className = '';
			for (var val of classes) {
				if (!val.includes('unit') || val === 'unit_icon') {
					icon.options.className += `${val} `;
				}
			}

			icon.options.className += `unit_${unit_id}`;
			return icon;
		}

		function UpdateMapMarkersList() {
			var car_units = [];
			var personal_units = [];
			var container_units = [];

			fetch(request)
			.then(response => response.json())
			.then(data => {
				for (var i = 0; i < data.length; ++i) { //location/last/map

					for (var k = 0; k < unitMarkers.length; ++k) { //unit/web/list

						if (unitMarkers[k].options.unitId == data[i].unitId) {

							if (data[i].lat && data[i].lon) {
								unitMarkers[k].setLatLng([data[i].lat, data[i].lon]).update();
								//unitMarkers[k].moveTo({lat: data[i].lat, lng: data[i].lon}, 100);
								unitMarkers[k].options.ignition = data[i].ignition;
								unitMarkers[k].options.status = data[i].status;
								unitMarkers[k].options.unitTypeId = data[i].unitTypeId;

								data[i]['state'] = unitMarkers[k].options.state;
								data[i]['collapsed'] = unitMarkers[k].options.collapsed;

								if (data[i].unitTypeId === 1) { // means unit is car
									
                  

                  if(data[i].status === 2){
                    unitMarkers[k].setIcon(append_unit_id(yellowCarIcon, data[i].unitId));
                  }
                  else if(data[i].status === 3 && data[i].direction > 0 && data[i].direction <= 22.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon1, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 22.5 && data[i].direction <= 45){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon2, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 45 && data[i].direction <= 67.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon3, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 67.5 && data[i].direction <= 90){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon4, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 90 && data[i].direction <= 112.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon5, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 112.5 && data[i].direction <= 135){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon6, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 135 && data[i].direction <= 157.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon7, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 157.5 && data[i].direction <= 180){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon8, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 180 && data[i].direction <= 202.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon9, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 202.5 && data[i].direction <= 225){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon10, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 225 && data[i].direction <= 247.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon11, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 247.5 && data[i].direction <= 270){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon12, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 270 && data[i].direction <= 292.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon13, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 292.5 && data[i].direction <= 315){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon14, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 315 && data[i].direction <= 337.5){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon15, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 3 && data[i].direction > 337.5 && data[i].direction <= 360){
                    unitMarkers[k].setIcon(append_unit_id(onlineCarIcon16, data[i].unitId));
                    // car direction
                  }
                  else if(data[i].status === 1){
                    unitMarkers[k].setIcon(append_unit_id(offlineCarIcon, data[i].unitId));
                  }
                  else{
                    unitMarkers[k].setIcon(append_unit_id(deactiveCarIcon, data[i].unitId));
                  }


									car_units.push(data[i]);
								}
							}
							else {
								console.warn("removed id " + unitMarkers[k].options.unitId);
								map.removeLayer(unitMarkers[k]);
								unitMarkers.splice(k, 1);
							}
						}
					}
				}

				//Zoom to fit all markers in map
				if (units_first_load && unitMarkers.length > 0) {

					var tmp_units = unitMarkers.filter(function (marker) {
						if (marker.getLatLng().lat !== 0) {
							return marker;
						}
					});
					var group = new L.featureGroup(tmp_units);

					map.fitBounds(group.getBounds());
					units_first_load = false;
				}

			}).catch(err => console.log(err));
		}

		$(function () {

			window.setInterval(function () {
				if (unitMarkers.length > 0) {
					UpdateMapMarkersList();
				}
			}, 1000);


			var btnUnitsPosition = null;

			$('#floatMenu').click(function () {
				$('#floatMenu').toggleClass('show');
				$('.floatMenu-nav').toggleClass('show');
				$('.btnMap').toggleClass('show');
				if ($('.units-btn-con').hasClass('show')) {
					$('.units-btn-con').toggleClass('show');
				}
			});


			$('#btnUnits').click(function () {

				$('.units-btn-con').toggleClass('show');
				if ($('.units-btn-con').hasClass('show') && !btnUnitsPosition) {
					btnUnitsPosition = $(this).position();
					$('.units-btn-con').css('top', btnUnitsPosition.top + 5);
					$('.units-btn-con').css('left', ($('body').width() - btnUnitsPosition.left) - 15);
				}
			});

			$('.unit-icon').click(function () {
				var img_src = $(this).find('img').attr('src');
				var target = $(this).data("target");
				if (img_src.includes('car-on')) {
					$(this).find('img').attr('src', img_src.replace('car-on', 'car-off'));
					$(".leaflet-marker-pane > img." + target).hide();
				}
				else {
					$(this).find('img').attr('src', img_src.replace('car-off', 'car-on'))
					$(".leaflet-marker-pane > img." + target).show();
				}

			});

		});

	</script>
</body>

</html>
var baselayersConf = [
  {
      "name": "google",
      "key": "google",
      "tileUrlTmpl": "https://mts{s}.google.com/vt/hl=tr&x={x}&y={y}&z={z}",
      "isElliptical": false,
      "subdomains": "0123",
      "minZoom": 4.5,
      "maxZoom": 22
  }
];

function quadkey(zyx) {
  var result = '';
  for (var i = zyx.z; i > 0; i--) {
      var digit = 0;
      var mask = 1 << (i - 1);
      if ((zyx.x & mask) != 0) {
          digit++;
      }
      if ((zyx.y & mask) != 0) {
          digit++;
          digit++;
      }
      result += digit;
  }
  return result;
}


var istanbulAltuf = L.latLng(41.0247173,29.0465252);
var ankaraAltuf = L.latLng(40.4246677, 32.2901031);
var units_first_load = true;

var map = L.map('map', {
    center: istanbulAltuf,
    zoomControl:false,
    zoom: 10,
    editable: true
});
L.control.scale().addTo(map);

/********************/

/********************/

var basemapsLayers = baselayersConf.map(function (conf) {
    return L.tileLayer(
        conf.tileUrlTmpl,
        L.Util.extend({quadkey: quadkey}, conf)
    );
});

var layersCtrl = L.control.layers(null, null, {collapsed: true ,position: 'topleft'}).addTo(map);
basemapsLayers.forEach(function (layer) {
  layersCtrl.addBaseLayer(layer, layer.options.name);
});

var deactiveCarIcon = L.icon({
  iconUrl: '/assets/images/deactive.svg',
  className:'deactive-car unit_icon'
});
var onlineCarIcon = L.icon({
  iconUrl: '/assets/images/online-car.png',
  className:'online-car unit_icon'
});
var offlineCarIcon = L.icon({
  iconUrl: '../assets/images/red-car.svg',
  className:'red-car unit_icon'
});
var yellowCarIcon = L.icon({
  iconUrl: '/assets/images/rolanti_1.svg',
  className:'rolanti-car unit_icon'
});

function initializUnits(){
  
  var token = '0b4bea0e-2b82-48d2-9e2e-4f040e79a953';
  var customer = undefined;
  

  var request = 'http://panel.mahrek.com.tr/'+token+'/location-last/map/list?customerId=' + customer;

  
  fetch(request)
    .then(response => response.json())
    .then(data => {
      console.log(data);

      marker.moveTo({
        lat: data[0],
        lng: data[1]
      }, 100);

      var latLngs = [ marker.getLatLng() ];
      var markerBounds = L.latLngBounds(latLngs);
      
    }).catch(err => console.log(err));
};

initializUnits();



map.addLayer(basemapsLayers[0]);

var marker = new L.Marker.movingMarker([[41.05861, 28.951141]]).addTo(map);
var i = 0;

function update_marker_position(){
  i += 0.00001;
  fetch("http://213.159.7.26:8090/receiver/getLatLong")
    .then(response => response.json())
    .then(data => {
      console.log(data);

      marker.moveTo({
        lat: data[0],
        lng: data[1]
      }, 100);

      var latLngs = [ marker.getLatLng() ];
      var markerBounds = L.latLngBounds(latLngs);
      
    }).catch(err => console.log(err));
}

window.onload = function(e){

  window.setInterval(function(){
    
   update_marker_position();
  }, 1000);
  
}
html, body, #map {
  margin: 0px;
  width: 100%;
  height: 100%;
  padding: 0px;
}
L.interpolatePosition = function(p1, p2, duration, t) {
    var k = t/duration;
    k = (k>0) ? k : 0;
    k = (k>1) ? 1 : k;
    return L.latLng(p1.lat + k*(p2.lat-p1.lat), p1.lng + k*(p2.lng-p1.lng));
};

L.Marker.MovingMarker = L.Marker.extend({

    //state constants
    statics: {
        notStartedState: 0,
        endedState: 1,
        pausedState: 2,
        runState: 3
    },

    options: {
        autostart: false,
        loop: false,
    },

    initialize: function (latlngs, durations, options) {
        L.Marker.prototype.initialize.call(this, latlngs[0], options);

        this._latlngs = latlngs.map(function(e, index) {    
            return L.latLng(e);
        });

        this._durations = durations;
        this._currentDuration = 0;
        this._currentIndex = 0;

        this._state = L.Marker.MovingMarker.notStartedState;
        this._startTime = 0;
        this._startTimeStamp = 0;
        this._pauseStartTime = 0;
        this._animId = 0;
        this._animRequested = false;
        this._currentLine = [];
    },

    isRunning: function() {
        return this._state === L.Marker.MovingMarker.runState;
    },

    isEnded: function() {
        return this._state === L.Marker.MovingMarker.endedState;
    },

    isStarted: function() {
        return this._state !== L.Marker.MovingMarker.notStartedState;
    },

    isPaused: function() {
        return this._state === L.Marker.MovingMarker.pausedState;
    },

    start: function() {
        if (this.isRunning()) {
            return;
        }

        if (this.isPaused()) {
            this.resume();
        } else {
            this._loadLine(0);
            this._startAnimation();
            this.fire('start');    
        } 
    },

    resume: function() {
        if (! this.isPaused()) {
            return;
        }
        // update the current line
        this._currentLine[0] = this.getLatLng(); 
        this._currentDuration -= (this._pauseStartTime - this._startTime);
        this._startAnimation();
    },

    addLatLng: function(latlng, duration) {
        this._latlngs.push(L.latLng(latlng));
        this._durations.push(duration);
    },

    moveTo: function(latlng, duration) {
        this._stopAnimation();
        this._latlngs = [this.getLatLng(), L.latLng(latlng)];
        this._durations = [duration];
        this._state = L.Marker.MovingMarker.notStartedState;
        this.start();
        this.options.loop = false;
    },

    addStation: function(pointIndex, duration) {
        if (pointIndex > this._latlngs.length - 2 || pointIndex < 1) {
            return;
        }
        var t = this._latlngs[pointIndex];
        this._latlngs.splice(pointIndex + 1, 0, t);
        this._durations.splice(pointIndex, 0, duration);
    },

    _startAnimation: function() {
        this._startTime = Date.now();
        this._state = L.Marker.MovingMarker.runState;
        this._animId = L.Util.requestAnimFrame(function(timestamp) {
            this._startTimeStamp = timestamp;
            this._animate(timestamp);
        }, this, true );
        this._animRequested = true;
    },

    _resumeAnimation: function() {
        if (! this._animRequested) {
            this._animId = L.Util.requestAnimFrame(function(timestamp) {
                this._animate(timestamp);
            }, this, true );
        }
    },

    _stopAnimation: function() {
        if (this._animRequested) {
            L.Util.cancelAnimFrame(this._animId);
            this._animRequested = false;
        }
    },

    _loadLine: function(index) {
        this._currentIndex = index;
        this._currentDuration = this._durations[index];
        this._currentLine = this._latlngs.slice(index, index + 2);
    },

    /**
     * Load the line where the marker is
     * @param  {Number} timestamp
     * @return {Number} elapsed time on the current line or null if
     * we reached the end or marker is at a station
     */
    _updateLine: function(timestamp) {
        //time elapsed since the last latlng
        var elapsedTime = timestamp - this._startTimeStamp;

        // not enough time to update the line
        if (elapsedTime <= this._currentDuration) {
            return elapsedTime;
        }

        var lineIndex = this._currentIndex;
        var lineDuration = this._currentDuration;

        while (elapsedTime > lineDuration) {
            //substract time of the current line
            elapsedTime -= lineDuration;
            lineIndex++;

            // test if we have reached the end of the polyline
            if (lineIndex >= this._latlngs.length - 1) {

                if (this.options.loop) {
                    lineIndex = 0;
                    this.fire('loop', {elapsedTime: elapsedTime});                        
                } else {
                    // place the marker at the end, else it would be at 
                    // the last position
                    this.setLatLng(this._latlngs[this._latlngs.length-1]);
                    this.stop(elapsedTime);
                    return null;
                }
            }
            lineDuration = this._durations[lineIndex];
        }

        this._loadLine(lineIndex);
        this._startTimeStamp = timestamp - elapsedTime;
        this._startTime = Date.now() - elapsedTime;
        return elapsedTime;
    },

    _animate: function(timestamp, noRequestAnim) {
        // compute the time elapsed since the start of the line
        var elapsedTime; 
        this._animRequested = false;

        //find the next line and compute the new elapsedTime
        elapsedTime = this._updateLine(timestamp);

        if (elapsedTime === null) {
            //we have reached the end
            return;
        }

        // compute the position
        var p = L.interpolatePosition(this._currentLine[0],
            this._currentLine[1],
            this._currentDuration,
            elapsedTime);
        this.setLatLng(p);

        if (! noRequestAnim) {
            this._animId = L.Util.requestAnimFrame(this._animate, this, false);
            this._animRequested = true;
        }
    },

    onAdd: function (map) {
        L.Marker.prototype.onAdd.call(this, map);

        if (this.options.autostart && (! this.isStarted())) {
            this.start();
            return;
        }

        if (this.isRunning()) {
            this._resumeAnimation();
        }
    },

    onRemove: function(map) {
        L.Marker.prototype.onRemove.call(this, map);
        this._stopAnimation();
    },

    pause: function() {
        if (! this.isRunning()) {
            return;
        }

        this._pauseStartTime = Date.now();
        this._state = L.Marker.MovingMarker.pausedState;
        this._stopAnimation();
        //force animation to place the marker at the right place
        this._animate(this._startTimeStamp
            + (this._pauseStartTime - this._startTime), true);
    },

    stop: function(elapsedTime) {
        if (this.isEnded()) {
            return;
        }

        this._stopAnimation();

        if (typeof(elapsedTime) === 'undefined') {
            //user call
            elapsedTime = 0;
            // force animation to place the marker at the right place
            this._animate(this._startTimeStamp
                + (Date.now() - this._startTime), true);
        }
        
        this._state = L.Marker.MovingMarker.endedState;
        this.fire('end', {elapsedTime: elapsedTime});
    }
});

L.Marker.movingMarker = function (latlngs, duration, options) {
    return new L.Marker.MovingMarker(latlngs, duration, options);
};