package pointstream;

import geometry.DoublePoint;
import geometry.DoubleVector;

import java.awt.Color;

import main.MathHelper;

public class LaplacianPointStream extends LinearPointStream 
{
	private final int WINDOW_SIZE = 4;

	public LaplacianPointStream(Color sColor, float thickness, double responsiveness, boolean isSpeedInfluenced) 
	{
		super(sColor, thickness, responsiveness, isSpeedInfluenced);
		type = LAPLACIAN_STREAM;
	}
	
	/**
     * Add a point to this stream using qStream as a guide curve and with the responsiveness provided.  If continueToEnd is true, if q(i+1) does not exist, then q(i+1) = qN. 
     * Returns true if a point was added.
     * @param qStream
     * @param responsiveness
     * @param continueToEnd
     * @return
     */
    public synchronized boolean addPoint(GuideStream qStream, boolean continueToEnd, boolean forceContinueToEnd, int forceContinueToEndQIndex)
    {
    	if(qStream == null || qStream.size() == 0 || qStream.size() == this.size())
    		return false;
    	
    	if(this.points.size() >= WINDOW_SIZE - 1)
    	{
    		if(this.size() < qStream.size())
    		{    	
    			// Add another point, so now we have have at least WINDOW_SIZE points to work with.
    			this.points.add(new GeneratedPoint(qStream.getPoint(this.size()), this.thickness/2.0f, this.size(), false));
    			
    			// Now we need to change the position of the before last point.
    			
    			for(int i = WINDOW_SIZE - 3; i >= 0; i--)
    			{
	    			GeneratedPoint p2 = this.points.get(this.size() - (WINDOW_SIZE - i -2));
	    			GeneratedPoint p1 = this.points.get(this.size() - (WINDOW_SIZE - i -1));
	    			GeneratedPoint p0 = this.points.get(this.size() - (WINDOW_SIZE - i));
	    			
	    			DoublePoint midPoint = new DoublePoint((p0.getX() + p2.getX())/2.0 , (p0.getY() + p2.getY())/2.0);
	    			
	    			double distanceBetween = p1.distanceTo(midPoint);//MathHelper.segmentDistance(p0, p2, p1);
	    			
	    			DoubleVector moveDirection = new DoubleVector(p1, midPoint);//p0p2;
	    			moveDirection.unit();
	    			
	    			double pointSpeed = qStream.getPoint(p1.getGuidePointIndex()).getSpeed();
	    			
	    			if(pointSpeed > 10.0)
	    				pointSpeed = 10.0;
	    			
	    			//double moveAmount = /*(1*pointSpeed)*/0.5*distanceBetween;
	    			double moveAmount = (0.2*pointSpeed)*distanceBetween;
	    			
//	    			System.out.println("p0 : " + p0);
//	    			System.out.println("p1 : " + p1);
//	    			System.out.println("p2 : " + p2);
//	    			System.out.println("Distance Between : " + distanceBetween);
//	    			System.out.println("Move Direction : " + moveDirection);
//	    			System.out.println("Point Speed : " + pointSpeed);
//	    			System.out.println("Speed Factor : " + (0.05*pointSpeed));
//	    			System.out.println("Move Amount : " + moveAmount);
//	    			System.out.println("-----------------");
	    			
	    			if(distanceBetween < LinearPointStream.DISTANCE_THRESHOLD)
	    				return true;
	    			
	    			double newX = p1.getX() + moveAmount*moveDirection.getX();
	    			double newY = p1.getY() + moveAmount*moveDirection.getY();
	    			
	    			GeneratedPoint newPoint = new GeneratedPoint(newX, newY, p1.getRadius(), p1.getGuidePointIndex(), p1.isContinueToEndPoint());
	    			this.points.set(this.size() - (WINDOW_SIZE - i - 1), newPoint);
    			}
    			
    		}
    	}
    	else
    	{
    		if(this.size() < qStream.size())
    			this.points.add(new GeneratedPoint(qStream.getPoint(this.size()), this.thickness/2.0f, this.size(), false));
    	}
    	
    	return true;
    }
	
}
