package speiger.src.coreengine.math;

public class MathUtils {
	private static final float[] SIN_TABLE;
	private static final float[] COS_TABLE;
	private static final float TABLE_MASK;
	private static final int SIN_MASK;
	public static final double HALF_PI = Math.PI * 0.5D;
	public static final double PI_INVERSION = 180D / Math.PI;
	
	public static float sin(float a) {
		return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK];
	}
	
	public static float sin(double a) {
		return SIN_TABLE[(int)(a * TABLE_MASK) & SIN_MASK];
	}
	
	public static float cos(float a) {
		return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK];
	}
	
	public static float cos(double a) {
		return COS_TABLE[(int)(a * TABLE_MASK) & SIN_MASK];
	}
	
	public static byte clamp(byte min, byte max, byte current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static short clamp(short min, short max, short current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static int clamp(int min, int max, int current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static float clamp(float min, float max, float current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static double clamp(double min, double max, double current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static long clamp(long min, long max, long current) {
		return current < min ? min : (current > max ? max : current);
	}
	
	public static byte sign(byte value) {
		return (byte)Integer.signum(value);
	}
	
	public static short sign(short value) {
		return (short)Integer.signum(value);
	}
	
	public static int sign(int value) {
		return Integer.signum(value);
	}
	
	public static long sign(long value) {
		return Long.signum(value);
	}
	
	public static float sign(float value) {
		return Math.signum(value);
	}
	
	public static double sign(double value) {
		return Math.signum(value);
	}
	
	public static int roundAlign(int value, int alignment) {
		if(alignment == 0) return value;
		int r = value % alignment;
		return r == 0 ? value : value + (alignment - r);
	}
	
	public static int sub(int key, int value) {
		return key - value;
	}
	
	public static int ceil(double value) {
		int i = (int)value;
		return value > i ? i + 1 : i;
	}
	
	public static int ceil(float value) {
		int i = (int)value;
		return value > i ? i + 1 : i;
	}
	
	public static int floor(float value) {
		int i = (int)value;
		return value < i ? i - 1 : i;
	}
	
	public static int floor(double value) {
		int i = (int)value;
		return value < i ? i - 1 : i;
	}
	
	public static int pow(int base, int exp) {
		return (int)Math.pow(base, exp);
	}
	
	public static float lerp(float start, float end, float progress) {
		return start + ((end - start) * progress);
	}
	
	public static double lerp(double start, double end, float progress) {
		return start + ((end - start) * progress);
	}
	
	public static float smoothLerp(float start, float end, float progress) {
		float cosProgress = (1F - MathUtils.cos(progress * Math.PI)) * 0.5F;
		return start * (1F - cosProgress) + end * cosProgress;
	}
	
	public static float quadraticCurve(float start, float median, float end, float progress) {
		return lerp(lerp(start, median, progress), lerp(median, end, progress), progress);
	}
	
	public static float smoothQuadraticCurve(float start, float median, float end, float progress) {
		return smoothLerp(smoothLerp(start, median, progress), smoothLerp(median, end, progress), progress);
	}
	
	static {
		SIN_MASK = ~(-1 << 12);
		int SIN_COUNT = SIN_MASK + 1;
		
		float radFull = (float)(Math.PI * 2D);
		float degFull = (float)(360.0);
		float radToIndex = SIN_COUNT / radFull;
		float degToIndex = SIN_COUNT / degFull;
		
		TABLE_MASK = radToIndex;
		SIN_TABLE = new float[SIN_COUNT];
		COS_TABLE = new float[SIN_COUNT];
		
		for(int i = 0;i < SIN_COUNT;i++) {
			SIN_TABLE[i] = (float)Math.sin((i + 0.5f) / SIN_COUNT * radFull);
			COS_TABLE[i] = (float)Math.cos((i + 0.5f) / SIN_COUNT * radFull);
		}
		for(int i = 0;i < 360;i += 90) {
			SIN_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.sin(i * Math.PI / 180D);
			COS_TABLE[(int)(i * degToIndex) & SIN_MASK] = (float)Math.cos(i * Math.PI / 180D);
		}
	}
}
