package speiger.src.coreengine.math.misc;

import java.util.function.Predicate;

import speiger.src.coreengine.math.vector.ints.Vec2i;

public enum Facing {
	NORTH(0, 2, "North", Axis.VERTICAL, Vec2i.of(0, 1)),
	EAST(1, 3, "East", Axis.HORIZONTAL, Vec2i.of(1, 0)),
	SOUTH(2, 0, "South", Axis.VERTICAL, Vec2i.of(0, -1)),
	WEST(3, 1, "West", Axis.HORIZONTAL, Vec2i.of(-1, 0));
	
	private static final Facing[] VALUES;
	private static final Facing[] ROTATIONS;
	final int index;
	final int rotationIndex;
	final String name;
	final Axis axis;
	final Vec2i offset;
	final boolean positive;
	final Rotation rotation;
	
	private Facing(int direction, int rotation, String display, Axis axis, Vec2i offset) {
		index = direction;
		rotationIndex = rotation;
		name = display;
		this.axis = axis;
		this.offset = offset;
		positive = index < 2;
		this.rotation = Rotation.fromFacing(this);
	}
	
	public int getIndex() { return index; }
	public boolean isPositive() { return positive; }
	
	public boolean isXAxis() { return axis == Axis.HORIZONTAL; }
	public boolean isZAxis() { return axis == Axis.VERTICAL; }
	public Axis getAxis() { return axis; }
	
	
	public Vec2i getOffset() { return offset; }	
	public float getMultiplier() { return positive ? 1F : -1F; }
	
	public String getName() { return name; }
	
	public int getRotationIndex() { return rotationIndex; }
	public int getRotation() { return rotationIndex * 90; }
	public int getRotation(Facing other) {
		if(other == backwards()) return getRotation() - 45;
		else if(other == forward()) return getRotation() + 45;
		return getRotation();
	}
	
	public Rotation toRotation() { return rotation; }
	public Facing rotate(int amount) { return byIndex(index + amount); }
	public Facing forward() { return byIndex(index + 1); }
	public Facing backwards() { return byIndex(index - 1); }
	public Facing opposite() { return byIndex(index + 2); }
	@Override
	public String toString() { return getName()+": "+offset; }
	
	public static Facing byIndex(int index) { return VALUES[index & 3]; }
	public static Facing byRotationIndex(int index) { return ROTATIONS[index & 3]; }
	public static Facing byYaw(float value) { return byRotationIndex((int)(value / 90) & 3); }
	
	static {
		Facing[] values = values();
		VALUES = new Facing[values.length];
		ROTATIONS = new Facing[values.length];
		for(Facing entry : values) {
			VALUES[entry.getIndex()] = entry;
			ROTATIONS[entry.getRotationIndex()] = entry;
		}
	}
	
	public static enum Rotation {
		NONE(Facing.NORTH),
		CLOCKWISE(Facing.EAST),
		OPPOSITE(Facing.SOUTH),
		COUNTER_CLOCKWISE(Facing.WEST);
		
		static final Rotation[] ROTATION = Rotation.values();
		Facing facing;
		
		private Rotation(Facing facing) {
			this.facing = facing;
		}
		
		public static Rotation fromFacing(Facing facing) { return ROTATION[facing.getIndex()]; }
		public Facing toFacing() { return facing; }
	}
	
	public static enum Axis implements Predicate<Facing> {
		HORIZONTAL(5),
		VERTICAL(10);
		
		int code;
		
		private Axis(int code) {
			this.code = code; 
		}
		public int getCode() { return code; }
		
		@Override
		public boolean test(Facing t) { return t.getAxis() == this; }
	}
	
}
