import java.util.ArrayList;

public class Loc {
	public final double x,y,z;

	public Loc() {this(0,0,0);}
	public Loc(int x, int z) {this(x,0,z);}
	public Loc(int x, int y, int z) {this((double)x,(double)y,(double)z);}
	public Loc(double x, double z) {this(x,0,z);}
	public Loc(fd world) {this(world.u().a,world.u().b,world.u().c);}
	public Loc(double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public int x() {return (int)x;}
	public int y() {return (int)y;}
	public int z() {return (int)z;}
	
	public Loc add(int x, int y, int z) { return new Loc(this.x+x,this.y+y,this.z+z); }
	public Loc add(double x, double y, double z) { return new Loc(this.x+x,this.y+y,this.z+z); }
	public Loc add(Loc other) { return new Loc(x+other.x,y+other.y,z+other.z); }
	
	public Loc subtract(int x, int y, int z) { return new Loc(this.x-x,this.y-y,this.z-z); }
	public Loc subtract(double x, double y, double z) { return new Loc(this.x-x,this.y-y,this.z-z); }
	public Loc subtract(Loc other) { return new Loc(x-other.x,y-other.y,z-other.z); }
	
	public Loc multiply(double xMult, double yMult, double zMult) { return new Loc(x*xMult,y*yMult,z*zMult); }
	
	public Loc getSide(int side) {
		if (side == 0) return new Loc(x,y-1,z);
	    if (side == 1) return new Loc(x,y+1,z);
	    if (side == 2) return new Loc(x,y,z-1);
	    if (side == 3) return new Loc(x,y,z+1);
	    if (side == 4) return new Loc(x-1,y,z);
	    if (side == 5) return new Loc(x+1,y,z);
	    return this;
	}

	public boolean equals(Object other) {
		if (other instanceof Loc) {
			Loc otherLoc = (Loc)other;
			return (x == otherLoc.x) && (y == otherLoc.y) && (z == otherLoc.z);
		}
		return false;
	}
	public String toString() {
		return "("+x+","+y+","+z+")";
	}
	
	public int distSimple(Loc other) {
		return (int)(Math.abs(x-other.x)+Math.abs(y-other.y)+Math.abs(z-other.z));
	}
	public double distAdv(Loc other) {
		return Math.sqrt(Math.pow(x-other.x,2)+Math.pow(y-other.y,2)+Math.pow(z-other.z,2));
	}
	
	public static Loc[] vecAdjacent() {
		Loc[] array = new Loc[6];
		array[0] = new Loc(0,0,1);
		array[1] = new Loc(0,0,-1);
		array[2] = new Loc(0,1,0);
		array[3] = new Loc(0,-1,0);
		array[4] = new Loc(1,0,0);
		array[5] = new Loc(-1,0,0);
		return array;
	}
	public Loc[] adjacent() {
		Loc[] array = vecAdjacent();
		for (int i = 0; i < array.length; i++) array[i] = add(array[i]);
		return array;
	}
	
	public static Loc[] vecAdjacent2D() {
		Loc[] array = new Loc[4];
		array[0] = new Loc(0,1);
		array[1] = new Loc(0,-1);
		array[2] = new Loc(1,0);
		array[3] = new Loc(-1,0);
		return array;
	}
	public Loc[] adjacent2D() {
		Loc[] array = vecAdjacent();
		for (int i = 0; i < array.length; i++) array[i] = add(array[i]);
		return array;
	}
	
	public static ArrayList<Loc> vecInRadius(int maxR, boolean advanced) {
		ArrayList<Loc> toReturn = new ArrayList<Loc>();
		Loc start = new Loc();
		for (int x = -maxR; x <= maxR; x++)
		for (int z = -maxR; z <= maxR; z++)
		for (int y = -maxR; y <= maxR; y++) {
			Loc check = new Loc(x,y,z);
			double dist = advanced ? start.distAdv(check) : start.distSimple(check);
			if (dist <= maxR) toReturn.add(check);
		}
		return toReturn;
	}
	public ArrayList<Loc> inRadius(int maxR, boolean advanced) {
		ArrayList<Loc> toReturn = new ArrayList<Loc>();
		for (int x = -maxR; x <= maxR; x++)
		for (int z = -maxR; z <= maxR; z++)
		for (int y = -maxR; y <= maxR; y++) {
			Loc check = new Loc(x,y,z).add(this);
			double dist = advanced ? distAdv(check) : distSimple(check);
			if (dist <= maxR) toReturn.add(check);
		}
		return toReturn;
	}
	
	public static ArrayList<Loc> vecInRadius2D(int maxR, boolean advanced) {
		ArrayList<Loc> toReturn = new ArrayList<Loc>();
		Loc start = new Loc();
		for (int x = -maxR; x <= maxR; x++)
		for (int z = -maxR; z <= maxR; z++) {
			Loc check = new Loc(x,z);
			double dist = advanced ? start.distAdv(check) : start.distSimple(check);
			if (dist <= maxR) toReturn.add(check);
		}
		return toReturn;
	}
	public ArrayList<Loc> inRadius2D(int maxR, boolean advanced) {
		ArrayList<Loc> toReturn = new ArrayList<Loc>();
		for (int x = -maxR; x <= maxR; x++)
		for (int z = -maxR; z <= maxR; z++) {
			Loc check = new Loc(x,z).add(this);
			double dist = advanced ? distAdv(check) : distSimple(check);
			if (dist <= maxR) toReturn.add(check);
		}
		return toReturn;
	}
	
	public int getBlock(xp blockAc) {return blockAc.a(x(),y(),z());}
	public Loc setBlockNotify(fd world, int blockID) {world.f(x(),y(),z(),blockID); return this;}
	public Loc setBlock(fd world, int blockID) {world.c(x(),y(),z(),blockID); return this;}
	
	public int getMeta(xp blockAc) {return blockAc.e(x(),y(),z());}
	public Loc setMeta(fd world, int meta) {world.e(x(),y(),z(),meta); return this;}
	public Loc setMetaNotify(fd world, int meta) {world.d(x(),y(),z(),meta); return this;}
	
	public Loc setBlockAndMeta(fd world, int blockID, int meta) {world.a(x(),y(),z(),blockID,meta); return this;}
	public Loc setBlockAndMetaNotify(fd world, int blockID, int meta) {world.b(x(),y(),z(),blockID,meta); return this;}
	
	public ow getTileEntity(xp blockAc) {return blockAc.b(x(),y(),z());}
	public Loc setTileEntity(fd world, ow tileEntity) {world.a(x(),y(),z(),tileEntity); return this;}
	public Loc removeTileEntity(fd world) {world.p(x(),y(),z()); return this;}
	
	public int getLight(fd world) {return world.m(x(),y(),z());}
	
	public Loc notify(fd world) {world.g(x(),y(),z(),getBlock(world)); return this;}
	public Loc setSpawnPoint(fd world) {world.a(new br(x(),y(),z())); return this;}
}