package erogenousbeef.bigreactors.common.multiblock.block;

import java.util.List;
import java.util.Random;

import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.Optional;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import erogenousbeef.bigreactors.common.BRLoader;
import erogenousbeef.bigreactors.common.BigReactors;
import erogenousbeef.bigreactors.common.multiblock.MultiblockTurbine;
import erogenousbeef.bigreactors.common.multiblock.interfaces.INeighborUpdatableEntity;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbineComputerPort;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbineFluidPort;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbineFluidPort.FluidFlow;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbinePartBase;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbinePartStandard;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbinePowerTap;
import erogenousbeef.bigreactors.common.multiblock.tileentity.TileEntityTurbineRotorBearing;
import erogenousbeef.bigreactors.utils.StaticUtils;
import erogenousbeef.core.common.CoordTriplet;
import erogenousbeef.core.multiblock.IMultiblockPart;
import erogenousbeef.core.multiblock.MultiblockControllerBase;
@Optional.InterfaceList({
	@Optional.Interface(iface = "dan200.computercraft.api.peripheral.IPeripheralProvider", modid = "ComputerCraft"),
})
public class BlockTurbinePart extends BlockContainer implements IPeripheralProvider {

	public static final int METADATA_HOUSING = 0;
	public static final int METADATA_CONTROLLER = 1;
	public static final int METADATA_POWERTAP = 2;
	public static final int METADATA_FLUIDPORT = 3;
	public static final int METADATA_BEARING = 4;
	public static final int METADATA_COMPUTERPORT = 5;
	
	private static final String[] _subBlocks = new String[] { "housing",
														"controller",
														"powerTap",
														"fluidPort",
														"bearing",
														"computerPort" };

	// Additional non-metadata-based icons
	private static final int SUBICON_NONE = -1;
	private static final int SUBICON_HOUSING_FRAME_TOP = 0;
	private static final int SUBICON_HOUSING_FRAME_BOTTOM = 1;
	private static final int SUBICON_HOUSING_FRAME_LEFT = 2;
	private static final int SUBICON_HOUSING_FRAME_RIGHT = 3;
	private static final int SUBICON_HOUSING_FACE = 4;
	private static final int SUBICON_HOUSING_CORNER = 5;
	private static final int SUBICON_CONTROLLER_IDLE = 6;
	private static final int SUBICON_CONTROLLER_ACTIVE = 7;
	private static final int SUBICON_POWERTAP_ACTIVE = 8;
	private static final int SUBICON_FLUIDPORT_OUTPUT = 9;

	private static final String[] _subIconNames = new String[] {
		"housing.edge.0",
		"housing.edge.1",
		"housing.edge.2",
		"housing.edge.3",
		"housing.face",
		"housing.corner",
		"controller.idle",
		"controller.active",
		"powerTap.connected",
		"fluidPort.outlet"
	};
	
	private IIcon[] _icons = new IIcon[_subBlocks.length];
	private IIcon[] _subIcons = new IIcon[_subIconNames.length];
	
	public BlockTurbinePart(Material material) {
		super(material);

		setStepSound(soundTypeMetal);
		setHardness(2.0f);
		setBlockName("blockTurbinePart");
		this.setBlockTextureName(BigReactors.TEXTURE_NAME_PREFIX + "blockTurbinePart");
		setCreativeTab(BigReactors.TAB);
	}

	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister par1IconRegister)
	{
		// Base icons
		for(int i = 0; i < _subBlocks.length; ++i) {
			_icons[i] = par1IconRegister.registerIcon(BigReactors.TEXTURE_NAME_PREFIX + getUnlocalizedName() + "." + _subBlocks[i]);
		}
		
		for(int i = 0; i < _subIcons.length; i++) {
			_subIcons[i] = par1IconRegister.registerIcon(BigReactors.TEXTURE_NAME_PREFIX + getUnlocalizedName() + "." + _subIconNames[i]);
		}
		
		this.blockIcon = _icons[0];
	}

	@Override
    public IIcon getIcon(IBlockAccess blockAccess, int x, int y, int z, int side) {
		TileEntity te = blockAccess.getTileEntity(x, y, z);
		int metadata = blockAccess.getBlockMetadata(x,y,z);

		if(te instanceof TileEntityTurbinePartBase) {
			TileEntityTurbinePartBase part = (TileEntityTurbinePartBase)te;
			MultiblockTurbine turbine = part.getTurbine();
			
			if(metadata == METADATA_FLUIDPORT) {
				if(te instanceof TileEntityTurbineFluidPort) {
					if(turbine == null || !turbine.isAssembled() || part.getOutwardsDir().ordinal() == side)
					{
						if(((TileEntityTurbineFluidPort)te).getFlowDirection() == FluidFlow.Out)
							return _subIcons[SUBICON_FLUIDPORT_OUTPUT];
						else
							return _icons[METADATA_FLUIDPORT];
					}
					else if(turbine.isAssembled() && part.getOutwardsDir().ordinal() != side)
						return _subIcons[SUBICON_HOUSING_FACE];
				}
				return getIcon(side, metadata);
			}
			else if(!part.isConnected() || turbine == null || !turbine.isAssembled()) {
				return getIcon(side, metadata);
			}
			else {
				int subIcon = SUBICON_NONE;
				if(metadata == METADATA_HOUSING) {
					subIcon = getSubIconForHousing(blockAccess, x, y, z, turbine, side);
				}
				else if(part.getOutwardsDir().ordinal() == side) {
					// Only put the fancy icon on one side of the machine. Other parts will use the base.
					if(metadata == METADATA_CONTROLLER) {
						if(turbine.getActive()) {
							subIcon = SUBICON_CONTROLLER_ACTIVE;
						}
						else {
							subIcon = SUBICON_CONTROLLER_IDLE;
						}
					}
					else if(metadata == METADATA_POWERTAP) {
						if(te instanceof TileEntityTurbinePowerTap && ((TileEntityTurbinePowerTap)te).isAttachedToPowerNetwork()) {
							subIcon = SUBICON_POWERTAP_ACTIVE;
						}
					}
				}
				else {
					// Assembled non-housing parts use the face texture so it's all smooth on the inside
					subIcon = SUBICON_HOUSING_FACE;
				}
				
				if(subIcon == SUBICON_NONE) {
					return getIcon(side, metadata);
				}
				else {
					return _subIcons[subIcon];
				}
			}
		}

		// Not a "proper" TE, so just pass through
		return getIcon(side, metadata);
	}
	
	private int getSubIconForHousing(IBlockAccess blockAccess, int x, int y, int z, MultiblockTurbine turbine, int side) {
		CoordTriplet minCoord, maxCoord;
		minCoord = turbine.getMinimumCoord();
		maxCoord = turbine.getMaximumCoord();
		
		if(minCoord == null || maxCoord == null) {
			return SUBICON_NONE;
		}
		
		int extremes = 0;
		boolean xExtreme, yExtreme, zExtreme;
		xExtreme = yExtreme = zExtreme = false;

		if(x == minCoord.x) { extremes++; xExtreme = true; }
		if(y == minCoord.y) { extremes++; yExtreme = true; }
		if(z == minCoord.z) { extremes++; zExtreme = true; }
		
		if(x == maxCoord.x) { extremes++; xExtreme = true; }
		if(y == maxCoord.y) { extremes++; yExtreme = true; }
		if(z == maxCoord.z) { extremes++; zExtreme = true; }

		if(extremes >= 3) {
			return SUBICON_HOUSING_CORNER;
		}
		else if(extremes <= 0) {
			return SUBICON_NONE;
		}
		else if(extremes == 1) {
			return SUBICON_HOUSING_FACE;
		}
		else {
			ForgeDirection[] dirsToCheck = StaticUtils.neighborsBySide[side];
			ForgeDirection dir;

			Block myBlock = blockAccess.getBlock(x, y, z);
			int iconIdx = -1;

			for(int i = 0; i < dirsToCheck.length; i++) {
				dir = dirsToCheck[i];
				
				Block neighborBlock = blockAccess.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ);
				// See if we're a turbine part
				if(neighborBlock != myBlock && neighborBlock != BigReactors.blockMultiblockGlass
						&& (BigReactors.blockMultiblockCreativePart != null && neighborBlock != BigReactors.blockMultiblockCreativePart)) {
					// One of these things is not like the others...
					iconIdx = i;
					break;
				}
			}
			
			return iconIdx + SUBICON_HOUSING_FRAME_TOP;
		}
	}

	@Override
	public IIcon getIcon(int side, int metadata) {
		metadata = Math.max(0, Math.min(metadata, _subBlocks.length-1));
		return _icons[metadata];
	}

	@Override
	public TileEntity createNewTileEntity(World world, int metadata) {
		if(metadata == METADATA_POWERTAP) {
			return new TileEntityTurbinePowerTap();
		}
		else if(metadata == METADATA_FLUIDPORT) {
			return new TileEntityTurbineFluidPort();
		}
		else if(metadata == METADATA_BEARING) {
			// Does jack-all different except for store display lists on the client
			return new TileEntityTurbineRotorBearing();
		}
		else if(metadata == METADATA_COMPUTERPORT) {
			return new TileEntityTurbineComputerPort();
		}
		else {
			return new TileEntityTurbinePartStandard();
		}
	}

	@Override
	public void onNeighborBlockChange(World world, int x, int y, int z, Block neighborBlock) {
		TileEntity te = StaticUtils.TE.getTileEntityUnsafe(world, x, y, z);

		// Signal power taps when their neighbors change, etc.
		if(te instanceof INeighborUpdatableEntity) {
			((INeighborUpdatableEntity)te).onNeighborBlockChange(world, x, y, z, neighborBlock);
		}
	}

	@Override
	public void onNeighborChange(IBlockAccess world, int x, int y, int z, int neighborX, int neighborY, int neighborZ) {
		TileEntity te = StaticUtils.TE.getTileEntityUnsafe(world, x, y, z);

		// Signal power taps when their neighbors change, etc.
		if(te instanceof INeighborUpdatableEntity) {
			((INeighborUpdatableEntity)te).onNeighborTileChange(world, x, y, z, neighborX, neighborY, neighborZ);
		}
	}


	@Override
	public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int par6, float par7, float par8, float par9) {
		if(player.isSneaking()) {
			return false;
		}
		
		int metadata = world.getBlockMetadata(x, y, z);
		
		if(metadata == METADATA_FLUIDPORT && StaticUtils.Inventory.isPlayerHoldingWrench(player)) {
			TileEntity te = world.getTileEntity(x, y, z);
			if(te instanceof TileEntityTurbineFluidPort) {
				TileEntityTurbineFluidPort fluidPort = (TileEntityTurbineFluidPort)te; 
				FluidFlow flow = fluidPort.getFlowDirection();
				fluidPort.setFluidFlowDirection(flow == FluidFlow.In ? FluidFlow.Out : FluidFlow.In, true);
				return true;
			}
		}
		
		if(world.isRemote) {
			return true;
		}
		
		// If the player's hands are empty and they rightclick on a multiblock, they get a 
		// multiblock-debugging message if the machine is not assembled.
		if(player.getCurrentEquippedItem() == null) {
			TileEntity te = world.getTileEntity(x, y, z);
			if(te instanceof IMultiblockPart) {
				MultiblockControllerBase controller = ((IMultiblockPart)te).getMultiblockController();

				if(controller == null) {
					player.addChatMessage(new ChatComponentText(String.format("SERIOUS ERROR - server part @ %d, %d, %d has no controller!", x, y, z))); //TODO Localize
				}
				else {
					Exception e = controller.getLastValidationException();
					if(e != null) {
						player.addChatMessage(new ChatComponentText(e.getMessage() + " - controller " + Integer.toString(controller.hashCode()))); //Would it be worth it to localize one word?
						return true;
					}
				}
			}
		}
		
		// Does this machine even have a GUI?
		if(metadata != METADATA_CONTROLLER) { return false; }

		// Check to see if machine is assembled
		TileEntity te = world.getTileEntity(x, y, z);
		if(!(te instanceof IMultiblockPart)) {
			return false;
		}
		
		IMultiblockPart part = (IMultiblockPart)te;
		if(!part.isConnected() || !part.getMultiblockController().isAssembled()) {
			return false;
		}
		
		player.openGui(BRLoader.instance, 0, world, x, y, z);
		return true;
	}

	@Override
	public boolean isOpaqueCube()
	{
		return true;
	}

	@Override
	public boolean renderAsNormalBlock()
	{
		return true;
	}

	@Override
	public int damageDropped(int metadata)
	{
		return metadata;
	}
	
	public ItemStack getItemStack(String name) {
		int metadata = -1;
		for(int i = 0; i < _subBlocks.length; i++) {
			if(_subBlocks[i].equals(name)) {
				metadata = i;
				break;
			}
		}
		
		if(metadata < 0) {
			throw new IllegalArgumentException("Unable to find a block with the name " + name);
		}
		
		return new ItemStack(this, 1, metadata);
	}
	
	@Override
	public void getSubBlocks(Item par1, CreativeTabs par2CreativeTabs, List par3List)
	{
		for(int i = 0; i < _subBlocks.length; i++) {
			par3List.add(new ItemStack(this, 1, i));
		}
	}
	
	@Override
	public void breakBlock(World world, int x, int y, int z, Block block, int meta)
	{
		// Drop everything inside inventory blocks
		TileEntity te = world.getTileEntity(x, y, z);
		if(te instanceof IInventory)
		{
			IInventory inventory = ((IInventory)te);
inv:		for(int i = 0; i < inventory.getSizeInventory(); i++)
			{
				ItemStack itemstack = inventory.getStackInSlot(i);
				if(itemstack == null)
				{
					continue;
				}
				float xOffset = world.rand.nextFloat() * 0.8F + 0.1F;
				float yOffset = world.rand.nextFloat() * 0.8F + 0.1F;
				float zOffset = world.rand.nextFloat() * 0.8F + 0.1F;
				do
				{
					if(itemstack.stackSize <= 0)
					{
						continue inv;
					}
					int amountToDrop = world.rand.nextInt(21) + 10;
					if(amountToDrop > itemstack.stackSize)
					{
						amountToDrop = itemstack.stackSize;
					}
					itemstack.stackSize -= amountToDrop;
					EntityItem entityitem = new EntityItem(world, (float)x + xOffset, (float)y + yOffset, (float)z + zOffset, new ItemStack(itemstack.getItem(), amountToDrop, itemstack.getItemDamage()));
					if(itemstack.getTagCompound() != null)
					{
						entityitem.getEntityItem().setTagCompound(itemstack.getTagCompound());
					}
					float motionMultiplier = 0.05F;
					entityitem.motionX = (float)world.rand.nextGaussian() * motionMultiplier;
					entityitem.motionY = (float)world.rand.nextGaussian() * motionMultiplier + 0.2F;
					entityitem.motionZ = (float)world.rand.nextGaussian() * motionMultiplier;
					world.spawnEntityInWorld(entityitem);
				} while(true);
			}
		}

		super.breakBlock(world, x, y, z, block, meta);
	}
	
    /**
     * A randomly called display update to be able to add particles or other items for display
     */
    @SideOnly(Side.CLIENT)
    public void randomDisplayTick(World world, int x, int y, int z, Random par5Random)
    {
    	int metadata = world.getBlockMetadata(x, y, z);
    	if(metadata == METADATA_BEARING) {
        	TileEntity te = world.getTileEntity(x, y, z);
    		if(te instanceof TileEntityTurbinePartStandard) {
    			// Rotor bearing found!
    			TileEntityTurbinePartStandard bearing = (TileEntityTurbinePartStandard)te;
    			MultiblockTurbine turbine = bearing.getTurbine();
    			if(turbine != null && turbine.getActive()) {
    				// Spawn particles!
    				int numParticles = Math.min(20, Math.max(1, turbine.getFluidConsumedLastTick() / 40));
    				ForgeDirection inwardsDir = bearing.getOutwardsDir().getOpposite();
    				CoordTriplet minCoord, maxCoord;
    				minCoord = turbine.getMinimumCoord();
    				maxCoord = turbine.getMaximumCoord();
    				minCoord.x++; minCoord.y++; minCoord.z++;
    				maxCoord.x--; maxCoord.y--; maxCoord.z--;
    				if(inwardsDir.offsetX != 0) {
    					minCoord.x = maxCoord.x = bearing.xCoord + inwardsDir.offsetX;
    				}
    				else if(inwardsDir.offsetY != 0) {
    					minCoord.y = maxCoord.y = bearing.yCoord + inwardsDir.offsetY;
    				}
    				else {
    					minCoord.z = maxCoord.z = bearing.zCoord + inwardsDir.offsetZ;
    				}
    				
                    double particleX, particleY, particleZ;
    				for(int i = 0; i < numParticles; i++) {
    					particleX = minCoord.x + par5Random.nextFloat() * (maxCoord.x - minCoord.x + 1);
    					particleY = minCoord.y + par5Random.nextFloat() * (maxCoord.y - minCoord.y + 1);
    					particleZ = minCoord.z + par5Random.nextFloat() * (maxCoord.z - minCoord.z + 1);
                        world.spawnParticle(BigReactors.isValentinesDay ? "heart" : "cloud", particleX, particleY, particleZ,
                        		par5Random.nextFloat() * inwardsDir.offsetX,
                        		par5Random.nextFloat() * inwardsDir.offsetY,
                        		par5Random.nextFloat() * inwardsDir.offsetZ);
    				}
    			}
    		}
    	}
    }
    
    @Override
    public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z)
    {
    	int metadata = world.getBlockMetadata(x, y, z);
    	if(metadata == METADATA_BEARING) {
        	TileEntity te = world.getTileEntity(x, y, z);
        	if(te instanceof TileEntityTurbineRotorBearing) {
        		TileEntityTurbineRotorBearing bearing = (TileEntityTurbineRotorBearing)te;
        		if(bearing.isConnected() && bearing.getTurbine().getActive()) {
        			return bearing.getAABB();
        		}
        	}
    	}
    	
		return super.getCollisionBoundingBoxFromPool(world, x, y, z);
    }

	@Override
    public boolean canCreatureSpawn(EnumCreatureType type, IBlockAccess world, int x, int y, int z)
    {
		return false;
    }

    // IPeripheralProvider
	@Optional.Method(modid = "ComputerCraft")
	@Override
	public IPeripheral getPeripheral(World world, int x, int y, int z, int side) {
		TileEntity te = world.getTileEntity(x, y, z);
		
		if(te instanceof TileEntityTurbineComputerPort)
			return (IPeripheral)te;
		
		return null;
	}
	
}
