package mekanism.api.gas;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import mekanism.api.util.CapabilityUtils;
import mekanism.common.capabilities.Capabilities;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

/**
 * A handy class containing several utilities for efficient gas transfer.
 * @author AidanBrady
 *
 */
public final class GasTransmission
{
	public static IGasHandler[] getConnectedAcceptors(TileEntity tileEntity, Collection<EnumFacing> sides)
	{
		return getConnectedAcceptors(tileEntity.getPos(), tileEntity.getWorld(), sides);
	}

	public static IGasHandler[] getConnectedAcceptors(BlockPos pos, World world, Collection<EnumFacing> sides)
	{
		IGasHandler[] acceptors = new IGasHandler[] {null, null, null, null, null, null};

		for(EnumFacing orientation : sides)
		{
			TileEntity acceptor = world.getTileEntity(pos.offset(orientation));

			if(CapabilityUtils.hasCapability(acceptor, Capabilities.GAS_HANDLER_CAPABILITY, orientation.getOpposite()))
			{
				acceptors[orientation.ordinal()] = CapabilityUtils.getCapability(acceptor, Capabilities.GAS_HANDLER_CAPABILITY, orientation.getOpposite());
			}
		}

		return acceptors;
	}
	
	/**
	 * Gets all the acceptors around a tile entity.
	 * @param tileEntity - center tile entity
	 * @return array of IGasAcceptors
	 */
	public static IGasHandler[] getConnectedAcceptors(TileEntity tileEntity)
	{
		return getConnectedAcceptors(tileEntity.getPos(), tileEntity.getWorld(), Arrays.asList(EnumFacing.VALUES));
	}

	public static IGasHandler[] getConnectedAcceptors(BlockPos pos, World world)
	{
		return getConnectedAcceptors(pos, world, Arrays.asList(EnumFacing.VALUES));
	}

	public static boolean isValidAcceptorOnSide(TileEntity tile, EnumFacing side)
	{
		if(CapabilityUtils.hasCapability(tile, Capabilities.GRID_TRANSMITTER_CAPABILITY, side.getOpposite()))
		{
			return false;
		}
		
		if(CapabilityUtils.hasCapability(tile, Capabilities.TUBE_CONNECTION_CAPABILITY, side.getOpposite()))
		{
			ITubeConnection connection = CapabilityUtils.getCapability(tile, Capabilities.TUBE_CONNECTION_CAPABILITY, side.getOpposite());
			
			if(connection.canTubeConnect(side.getOpposite()))
			{
				return true;
			}
		}

		return false;
	}

	/**
	 * Removes a specified amount of gas from an IGasItem.
	 * @param itemStack - ItemStack of the IGasItem
	 * @param type - type of gas to remove from the IGasItem, null if it doesn't matter
	 * @param amount - amount of gas to remove from the ItemStack
	 * @return the GasStack removed by the IGasItem
	 */
	public static GasStack removeGas(ItemStack itemStack, Gas type, int amount)
	{
		if(itemStack != null && itemStack.getItem() instanceof IGasItem)
		{
			IGasItem item = (IGasItem)itemStack.getItem();

			if(type != null && item.getGas(itemStack) != null && item.getGas(itemStack).getGas() != type || !item.canProvideGas(itemStack, type))
			{
				return null;
			}

			return item.removeGas(itemStack, amount);
		}

		return null;
	}

	/**
	 * Adds a specified amount of gas to an IGasItem.
	 * @param itemStack - ItemStack of the IGasItem
	 * @param stack - stack to add to the IGasItem
	 * @return amount of gas accepted by the IGasItem
	 */
	public static int addGas(ItemStack itemStack, GasStack stack)
	{
		if(itemStack != null && itemStack.getItem() instanceof IGasItem && ((IGasItem)itemStack.getItem()).canReceiveGas(itemStack, stack.getGas()))
		{
			return ((IGasItem)itemStack.getItem()).addGas(itemStack, stack.copy());
		}

		return 0;
	}
	
	/**
	 * Emits gas from a central block by splitting the received stack among the sides given.
	 * @param stack - the stack to output
	 * @param from - the TileEntity to output from
	 * @param sides - the list of sides to output from
	 * @return the amount of gas emitted
	 */
	public static int emit(GasStack stack, TileEntity from, Collection<EnumFacing> sides)
	{
		if(stack == null)
		{
			return 0;
		}
		
		List<IGasHandler> availableAcceptors = new ArrayList<IGasHandler>();
		IGasHandler[] possibleAcceptors = getConnectedAcceptors(from, sides);
		
		for(int i = 0; i < possibleAcceptors.length; i++)
		{
			IGasHandler handler = possibleAcceptors[i];
			
			if(handler != null && handler.canReceiveGas(EnumFacing.getFront(i).getOpposite(), stack.getGas()))
			{
				availableAcceptors.add(handler);
			}
		}

		Collections.shuffle(availableAcceptors);

		int toSend = stack.amount;
		int prevSending = toSend;

		if(!availableAcceptors.isEmpty())
		{
			int divider = availableAcceptors.size();
			int remaining = toSend % divider;
			int sending = (toSend-remaining)/divider;

			for(IGasHandler acceptor : availableAcceptors)
			{
				int currentSending = sending;

				if(remaining > 0)
				{
					currentSending++;
					remaining--;
				}
				
				EnumFacing dir = EnumFacing.getFront(Arrays.asList(possibleAcceptors).indexOf(acceptor)).getOpposite();
				toSend -= acceptor.receiveGas(dir, new GasStack(stack.getGas(), currentSending), true);
			}
		}

		return prevSending-toSend;
	}
}
