/**
 * APIMethod: createDirection
 * Create dirction symbol point {<openLayers.Feature.Vector>} of the line 
 * with attribute as angle (degree) for given position(s) on line 
 * Parameter:
 * line - {<OpenLayers.Geometry.LineString>} or {<OpenLayers.Geometry.MultiLineString>}
 * postion - {string} 
 *		"start" - start of the geometry / segment
 *		"end" - end of geometry / segment
 *		"middle" - middle of geometry /segment
 * forEachSegment - {boolean}
 *	if true create points on each segments of line for given position
 */

createDirection = function(line,position,forEachSegment) {
	if (line instanceof OpenLayers.Geometry.MultiLineString) {
		//TODO
	} else if (line instanceof OpenLayers.Geometry.LineString) {
		return createLineStringDirection(line,position,forEachSegment);
	} else {
		return [];
	}
};

createLineStringDirection = function(line,position, forEachSegment){
	if (position == undefined ){ position ="end"}
	if (forEachSegment == undefined ) {forEachSegment = false;}
	var points =[];
	//var allSegs = line.getSortedSegments();
	var allSegs = getSegments(line);
	var segs = [];

	if (forEachSegment)	{		
		segs = allSegs;
	} else {
		if  (position == "start") {
			segs.push(allSegs[0]);
		} else if (position == "end") {
			segs.push(allSegs[allSegs.length-1]);
		} else if (position == "middle"){
			return [getPointOnLine(line,.5)];
		} else {
			return [];
		}
	}
	for (var i=0;i<segs.length ;i++ )	{
		points = points.concat(createSegDirection(segs[i],position) );
	}
	return points;
};

createSegDirection = function(seg,position) {
	var segBearing = bearing(seg);
	var positions = [];
	var points = [];
	if  (position == "start") {
		positions.push([seg.x1,seg.y1]);
	} else if (position == "end") {
		positions.push([seg.x2,seg.y2]);
	} else if (position == "middle") {
		positions.push([(seg.x1+seg.x2)/2,(seg.y1+seg.y2)/2]);
	} else {
		return null;
	}
	for (var i=0;i<positions.length;i++ ){
		var pt = new OpenLayers.Geometry.Point(positions[i][0],positions[i][1]);
		var ptFeature = new OpenLayers.Feature.Vector(pt,{angle:segBearing}); 
		points.push(ptFeature);
	}
	return points;	
};

bearing = function(seg) {
	b_x = 0;
	b_y = 1;
	a_x = seg.x2 - seg.x1;
	a_y = seg.y2 - seg.y1;
	angle_rad = Math.acos((a_x*b_x+a_y*b_y)/Math.sqrt(a_x*a_x+a_y*a_y)) ;
	angle = 360/(2*Math.PI)*angle_rad;
	if (a_x < 0) {
	    return 360 - angle;
	} else {
	    return angle;
	}
};

getPointOnLine = function (line,measure) {
    var segs = getSegments(line);
    var lineLength = line.getLength();
    var measureLength = lineLength*measure;
    var length = 0;
	var partLength=0;
    for (var i=0;i<segs.length ;i++ ) {
        var segLength = getSegmentLength(segs[i]);        
        if (measureLength < length + segLength) {
			partLength = measureLength - length;
			var x = segs[i].x1 + (segs[i].x2 - segs[i].x1) * partLength/segLength;
			var y = segs[i].y1 + (segs[i].y2 - segs[i].y1) * partLength/segLength;
			var segBearing = bearing(segs[i]);
			console.log("x: " + x+", y: " + y + ", bearing: " + segBearing);
			var pt = new OpenLayers.Geometry.Point(x,y);
			var ptFeature = new OpenLayers.Feature.Vector(pt,{angle:segBearing}); 
			return ptFeature;
        } 
		length = length + segLength;
    }
	return false;
};

getSegmentLength = function(seg) {
    return Math.sqrt( Math.pow((seg.x2 -seg.x1),2) + Math.pow((seg.y2 -seg.y1),2) );
};

getSegments = function(line) {	
	var numSeg = line.components.length - 1;
	var segments = new Array(numSeg), point1, point2;
	for(var i=0; i<numSeg; ++i) {
	    point1 = line.components[i];
	    point2 = line.components[i + 1];
	    segments[i] = {
	        x1: point1.x,
	        y1: point1.y,
	        x2: point2.x,
	        y2: point2.y
	    };
	}
	return segments;
};


/**
 * OpenLayers Line Directions Layer
 */
OpenLayers.Layer.LineDirections =
	  OpenLayers.Class(OpenLayers.Layer.Vector, {
	    /**
	     * TODO Kommentieren
	     */
	    initialize: function(options) {
		  // Zun?chst den Konstruktor der Elternklasse aufrufen
		  OpenLayers.Layer.Vector.prototype.initialize.apply(this, arguments);
		  this.sourceLayer.events.on({
	        featuresadded: this.rebuildDirections,
	        featuresremoved: this.rebuildDirections,
	        afterfeaturemodified: this.rebuildDirections,
	        loadend: this.rebuildDirections,
	        refresh: this.rebuildDirections,
	        scope: this
	      });
	      // Die Richtungsfeatures erzeugen
	      this.buildDirections();
	    },
	    /**
	     * TODO Kommentieren
	     */
	    rebuildDirections: function() {
	      this.destroyFeatures();
	      this.buildDirections();
	    },
	    /**
	     * TODO Kommentieren
	     */
	    buildDirections: function() {
	      var directionFeatures
	      for(var i = 0,len = this.sourceLayer.features.length; i < len; i++){
	          var feature_geom = this.sourceLayer.features[i].geometry;
	          directionFeatures = this.getDirectionFeatures(feature_geom);
	          this.addFeatures( directionFeatures );
	      }
	    },

	    /**
	     * Die einzelnen Features aus einem Linestring extrahieren
	     */
	    getDirectionFeatures: function(line_geometry) {
	      var directionFeatures = [];
	      var vertices = line_geometry.getVertices();
	      for (var i=0, len = vertices.length; i < len; i++) {
	        if (i+1 < len) {
	          var line = new OpenLayers.Geometry.LineString([
	            vertices[i],
	            vertices[i+1]
	          ]);
	          var p_geom = this.getLineCentroid(line);
	          var lineangle = this.determineDirection( line );
	          var p_feature = new OpenLayers.Feature.Vector(
	            p_geom,
	            {
	              angle: lineangle
	            }
	          );
	          directionFeatures.push( p_feature );
	        }
	      }
	      return directionFeatures;
	    },
	    /**
	     * TODO Kommentieren
	     */
	    determineDirection: function(line) {
	      var endpoints = line.getVertices(true);
	      var piHalf = Math.PI / 2;
	      var radian;
	      if (endpoints.length === 2) {
	        var delta_x = endpoints[0].x - endpoints[1].x;
	        var delta_y = endpoints[0].y - endpoints[1].y;
	        if (delta_y != 0) {
	          if (delta_x != 0) {
	            var relation = delta_y / delta_x;
	            // Wir m?ssen den arctan noch
	            // mit -1 multiplizieren, da im
	            // Uhrzeigersinn gedreht wird!
	            radian = Math.atan( relation ) * -1;
	            // Korrigieren:
	            if (delta_x > 0) {
	                // zeigt nach links
	                radian += Math.PI;
	            }
	          } else {
	            // pfeil zeigt nach oben oder unten...
	            if ( delta_y > 0 ) {
	              // ... nach unten
	              radian = piHalf;
	            } else {
	              // ... nach oben
	              radian = Math.PI + piHalf;
	            }
	          }
	        } else {
	          // pfeil zeigt nach rechts oder links...
	          if ( delta_x > 0 ) {
	             // ... nach links
	             radian = Math.PI;
	          } else {
	             // ... nach rechts
	             radian = 0;
	          }
	        }
	      }
	      // return as degree
	      return (radian * (180 / Math.PI));
	    },

	    /**
	     * Bestimmt den Schwerpunkt / die Mitte einer
	     * Liniengeometrie mit zwei Punkten
	     */
	    getLineCentroid: function(line){
	      var endpoints = line.getVertices( );
	      var centroid;
	      if (endpoints.length === 2) {
	        var x = ( endpoints[0].x + endpoints[1].x ) / 2;
	        var y = ( endpoints[0].y + endpoints[1].y ) / 2;
	        centroid = new OpenLayers.Geometry.Point( x, y );
	      }
	      return centroid;
	    },
	    CLASS_NAME: "OpenLayers.Layer.LineDirections2"
	});