package net.torocraft.toroquest.generation;

import java.util.Random;

import net.minecraft.block.BlockBush;
import net.minecraft.block.BlockChest;
import net.minecraft.block.BlockStone;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.init.Blocks;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
import net.minecraft.world.storage.loot.LootTableList;
import net.torocraft.toroquest.entities.EntityBas;

public class BastionsLairGenerator extends WorldGenerator {

	private final int width = 40;
	private final int height = 25;

	private BlockPos origin;
	private int x, y, z;
	private World world;
	private Random rand;
	private IBlockState block;

	@Override
	public boolean generate(World world, Random rand, BlockPos pos) {
		this.world = world;
		this.rand = rand;

		findOrigin(pos);

		if (origin == null) {
			return false;
		}

		genMainChamber();
		genEntrance(width + 2, 0, EnumFacing.WEST);
		genEntrance(0, -width - 2, EnumFacing.NORTH);
		genEntrance(0, width + 2, EnumFacing.SOUTH);
		genEntrance(-width - 2, 0, EnumFacing.EAST);
		spawnBas();
		return true;
	}

	protected void findOrigin(BlockPos pos) {
		origin = null;
		BlockPos a = getSurfacePosition(pos.add(width, 0, 0));
		BlockPos b = getSurfacePosition(pos.add(0, 0, -width));
		BlockPos c = getSurfacePosition(pos.add(0, 0, width));
		BlockPos d = getSurfacePosition(pos.add(-width, 0, 0));

		if (a == null || b == null || c == null || d == null) {
			return;
		}

		origin = a;

		if (b.getY() < origin.getY()) {
			origin = b;
		}

		if (c.getY() < origin.getY()) {
			origin = c;
		}

		if (d.getY() < origin.getY()) {
			origin = d;
		}

		origin = origin.down(50);

		if (origin.getY() < 5) {
			origin = new BlockPos(origin.getX(), 5, origin.getZ());
		}
	}

	protected void genEntrance(int x, int z, EnumFacing facing) {
		BastionsLairEntranceGenerator g = new BastionsLairEntranceGenerator();
		g.setEntrance(facing);
		g.generate(world, rand, new BlockPos(x, walkwayHeight, z).add(origin));
	}

	private BlockPos getSurfacePosition(BlockPos start) {
		IBlockState blockState;
		BlockPos search = new BlockPos(start.getX(), world.getActualHeight(), start.getZ());
		while (search.getY() > 0) {
			search = search.down();
			blockState = world.getBlockState(search);
			if (isLiquid(blockState)) {
				return null;
			}
			if (isGroundBlock(blockState)) {
				break;
			}
		}
		return search;
	}

	private boolean isLiquid(IBlockState blockState) {
		return blockState.getBlock() == Blocks.WATER || blockState.getBlock() == Blocks.LAVA;
	}

	private boolean isGroundBlock(IBlockState blockState) {
		if (blockState.getBlock() == Blocks.LEAVES || blockState.getBlock() == Blocks.LEAVES2 || blockState.getBlock() == Blocks.LOG || blockState.getBlock() instanceof BlockBush) {
			return false;
		}
		return blockState.isOpaqueCube();
	}

	private void genMainChamber() {
		for (y = 0; y <= height; y++) {
			for (x = -width; x <= width; x++) {
				for (z = -width; z <= width; z++) {
					placeTombBlock();
				}
			}
		}
	}

	private int radiusSq;
	private final int walkwayHeight = height - 10;

	protected void placeTombBlock() {
		block = null;

		radiusSq = (x * x) + (z * z);

		if (isOutside()) {
			return;

		} else if (isWall() || isRoof()) {
			block = Blocks.STONE.getDefaultState();

		} else if (isFloor()) {
			block = Blocks.DIRT.getDefaultState();

		} else if (isPlatform()) {

			if (isPlatformUnderstructure()) {
				if (isHiddenUnderPlatformChest()) {
					block = Blocks.CHEST.getDefaultState();
				} else {
					block = Blocks.DIRT.getDefaultState();
				}
			} else if (isPlatformEdge()) {
				block = Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.DIORITE_SMOOTH);
			} else {
				block = Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.ANDESITE_SMOOTH);
			}

		} else if (isWalkway()) {

			if (isWalkwayEdge()) {
				block = Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.DIORITE_SMOOTH);
			} else {
				block = Blocks.STONE.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.ANDESITE_SMOOTH);
			}

		} else if (isWalkwayTorch()) {
			block = Blocks.REDSTONE_TORCH.getDefaultState();

		} else if (isWalkwaySubstructure()) {
			block = Blocks.DIRT.getDefaultState();

		} else if (isLootChest()) {
			block = randomChest();
		} else {
			block = Blocks.AIR.getDefaultState();
		}

		placeBlock();
		addLootToChest();
	}

	protected boolean isHiddenUnderPlatformChest() {
		return y == 1 && x == 0 && z == 0;
	}

	private IBlockState randomChest() {
		int roll = rand.nextInt(4);
		switch (roll) {
		case 1:
			return Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, EnumFacing.NORTH);
		case 2:
			return Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, EnumFacing.SOUTH);
		case 3:
			return Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, EnumFacing.EAST);
		default:
			return Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, EnumFacing.WEST);
		}
	}

	protected void addLootToChest() {
		if (block == null) {
			return;
		}
		if (block.getBlock() == Blocks.CHEST) {
			TileEntity tileentity = world.getTileEntity(origin.add(x, y, z));
			if (tileentity instanceof TileEntityChest) {
				((TileEntityChest) tileentity).setLootTable(LootTableList.CHESTS_END_CITY_TREASURE, world.rand.nextLong());
			}
		}
	}

	private boolean isLootChest() {
		return y == 1 && rand.nextInt(400) == 0;
	}

	private boolean isPlatformUnderstructure() {
		return y < walkwayHeight;
	}

	protected boolean isPlatform() {
		return y <= walkwayHeight && radiusSq < 55;
	}

	protected boolean isPlatformEdge() {
		return y == walkwayHeight && radiusSq > 45;
	}

	private boolean isWalkwayEdge() {
		if (Math.abs(x) <= 3 && Math.abs(z) <= 3) {
			return false;
		}
		return Math.abs(x) == 3 || Math.abs(z) == 3;
	}

	private boolean isWalkwaySubstructure() {

		if (y >= walkwayHeight || y < walkwayHeight - 5) {
			return false;
		}

		if (!(Math.abs(x) < 4 || Math.abs(z) < 4)) {
			return false;
		}

		int distanceUnderWalkway = walkwayHeight - y;

		if (distanceUnderWalkway > 5 || distanceUnderWalkway < 1) {
			return false;
		}

		int distanceFromWalkwayMiddle = Math.min(Math.abs(z), Math.abs(x));

		return distanceFromWalkwayMiddle + distanceUnderWalkway < 5;
	}

	private boolean isWalkway() {
		return y == walkwayHeight && (Math.abs(x) < 4 || Math.abs(z) < 4);
	}

	private boolean isWalkwayTorch() {
		return y == walkwayHeight + 1 && isWalkwayEdge() && (x % 6 == 0 || z % 6 == 0) && (Math.abs(x) > 4 || Math.abs(z) > 4);
	}

	private boolean isFloor() {
		return y == 0;
	}

	private boolean isRoof() {
		return y == height;
	}

	protected void placeBlock() {
		if (block == null) {
			return;
		}
		setBlockAndNotifyAdequately(world, origin.add(x, y, z), block);
	}

	private boolean isWall() {
		return Math.abs(x) + Math.abs(z) == width + 4;
	}

	protected boolean isOutside() {
		return Math.abs(x) + Math.abs(z) > width + 4;
	}

	private void spawnBas() {
		EntityBas e = new EntityBas(world);
		e.setPosition(origin.getX() + 0.5, origin.getY() + walkwayHeight + 1, origin.getZ() + 0.5);
		e.onInitialSpawn(world.getDifficultyForLocation(new BlockPos(e)), (IEntityLivingData) null);
		world.spawnEntity(e);
	}

}