using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SplineGenerator {

	public static CurvySpline GenerateSpline(int numberOfPoints, float radius, float minSegmentLength, float maxSegmentLength)
	{
		Vector3[] controlPoints = GenerateSplinePositions(numberOfPoints, radius, minSegmentLength, maxSegmentLength);
		return GenerateSplineFromPositions (controlPoints);
	}

	public static CurvySpline GenerateSplineFromPositions(Vector3[] controlPoints)
	{
		GameObject splineGo = new GameObject ("spline");
		CurvySpline spline = splineGo.AddComponent<CurvySpline> ();
		spline.Add (controlPoints);
		spline.Closed = false;
		return spline;
	}

	public static Vector3[] GenerateSplinePositions(int numberOfPoints, float radius, float minSegmentLength, float maxSegmentLength)
	{ 
		Vector3[] controlPoints = new Vector3[numberOfPoints];
		Vector3 lastPosition = Vector3.zero;
		for (int i=0; i<numberOfPoints; i++) {
			Vector3 randomPositionInsideSphere = Random.insideUnitSphere * radius;
			if(Vector3.Distance(lastPosition, randomPositionInsideSphere) > maxSegmentLength)
			{
				randomPositionInsideSphere = lastPosition + (randomPositionInsideSphere - lastPosition).normalized * maxSegmentLength;
			}
			else if(Vector3.Distance(lastPosition, randomPositionInsideSphere) < minSegmentLength)
			{
				randomPositionInsideSphere = lastPosition + (randomPositionInsideSphere - lastPosition).normalized * minSegmentLength;
			}
			controlPoints[i] = randomPositionInsideSphere;
			lastPosition = randomPositionInsideSphere;
		}

		return controlPoints;
	}

	public static Vector3[] GenerateSplinePositions2(int numberOfPoints, float radius, float minSegmentLength, float maxSegmentLength)
	{
		Vector3[] controlPoints = new Vector3[numberOfPoints];
		Vector3 lastPosition = Vector3.zero;
		controlPoints [0] = Vector3.zero;
		for (int i=1; i<numberOfPoints; i++) {
			Vector3 direction = i == 1 ? Vector3.forward : (lastPosition - controlPoints[i-2]).normalized;
			float previousXAngle = Mathf.Acos(direction.z / (Mathf.Sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z)));
			float previousYAngle = Mathf.Atan2(direction.y, direction.x);
			float distance = Random.Range(maxSegmentLength * 0.5f, maxSegmentLength);
			float xAngle = previousXAngle + Random.value * 60 * Mathf.Deg2Rad;
			float yAngle = previousYAngle + Random.value * 60 * Mathf.Deg2Rad;
//			float xAngle = Random.value * 60 * Mathf.Deg2Rad;
//			float yAngle = Random.value * 60 * Mathf.Deg2Rad;
			float x = distance * Mathf.Cos(xAngle) * Mathf.Sin(yAngle);
			float y = distance * Mathf.Sin(xAngle) * Mathf.Sin(yAngle);
			float z = distance * Mathf.Cos(yAngle);
			Vector3 position = lastPosition + new Vector3(x,y,z);
			controlPoints[i] = position;
			lastPosition = position;
		}

		return controlPoints;
	}

	public static Vector3[] RelaxSplinePositions(Vector3[] positions, float smoothness)
	{
		Vector3[] forces = new Vector3[positions.Length];

		for(int x=0; x<positions.Length; x++)
		{
			for(int y=x+1; y<positions.Length; y++)
			{
				float distance = Vector3.Distance(positions[x], positions[y]);
				float force = 1f / (distance * distance);
				force *= 0.01f;
				Vector3 directionX = (positions[x] - positions[y]).normalized;
				Vector3 directionY = (positions[y] - positions[x]).normalized;
				forces[x] += force * directionX;
				forces[y] += force * directionY;
			}
		}

		for (int i=0; i<positions.Length; i++) {
			positions[i] += forces[i];
		}

		return positions;
	}

	public static Vector3[] SmoothSplinePositions(Vector3[] positions, float smoothness)
	{
		Vector3[] smoothedPositions = new Vector3[positions.Length];

		for (int i=1; i<positions.Length - 1; i++) {
			Vector3 pos = positions [i];
			Vector3 previousPos = positions [i - 1];
			Vector3 nextPos = positions [i + 1];
			Vector3 midPoint = (previousPos + nextPos) * 0.5f;
			float previousNextDistance = Vector3.Distance(previousPos, nextPos);
			float height = Vector3.Distance(pos, midPoint);
			float ratio = height / previousNextDistance;
			if(ratio > 0.7f)
			{
				pos = midPoint + (pos - midPoint) * 0.7f;
			}
			smoothedPositions [i] = pos;
		}
		smoothedPositions [0] = positions [0];
		smoothedPositions [positions.Length - 1] = positions [positions.Length - 1];
		for(int i=0; i<positions.Length; i++)
		{
			positions[i] = smoothedPositions[i];
		}
		return positions;
	}
}
