package ihl.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import ihl.worldgen.ores.IHLFluid;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidTank;

public class IHLFluidTank implements IFluidTank
{
    protected List<FluidStack> fluidList = new ArrayList<FluidStack>();
    protected int capacity;
    protected boolean isOpenVessel=false;

    public IHLFluidTank(int capacity)
    {
        this.capacity = capacity;
    }

    public IHLFluidTank(int capacity, boolean isOpenVessel1)
    {
    	this.isOpenVessel=isOpenVessel1;
        this.capacity = capacity;
    }
    
    public IHLFluidTank readFromNBT(NBTTagCompound nbt)
    {
        if (!nbt.hasKey("Empty"))
        {
    		NBTTagList fluidList1 = nbt.getTagList("fluids", 10);
            for(int i=0;i<fluidList1.tagCount();i++)
            {
            	NBTTagCompound fluidNBT1 = fluidList1.getCompoundTagAt(i);
            	FluidStack fluid = FluidStack.loadFluidStackFromNBT(fluidNBT1);
                if (fluid != null)
                {
                	fluidList.add(fluid);
                }
            }
        }
        return this;
    }

    public NBTTagCompound writeToNBT(NBTTagCompound nbt)
    {
        if (!fluidList.isEmpty())
        {
        	NBTTagList fluids = new NBTTagList();
        	Iterator<FluidStack> fli = fluidList.iterator();
			while(fli.hasNext())
        	{
	        	FluidStack fluid=fli.next();
        		NBTTagCompound fluidNBT1 = new NBTTagCompound();
                fluid.writeToNBT(fluidNBT1);
                fluids.appendTag(fluidNBT1);
        	}
			nbt.setTag("fluids", fluids);
        }
        else
        {
            nbt.setString("Empty", "");
        }
        return nbt;
    }

    /* IFluidTank */
    @Override
    public FluidStack getFluid()
    {
    	if(this.fluidList.isEmpty())
    	{
    		return null;
    	}
        return this.fluidList.get(0);
    }

    @Override
    public int getFluidAmount()
    {
    	int amount=0;
    	Iterator<FluidStack> fli = fluidList.iterator();
		while(fli.hasNext())
    	{
        	FluidStack fluid=fli.next();
        	amount+=fluid.amount;
    	}
        return amount;
    }

    @Override
    public int getCapacity()
    {
        return capacity;
    }

    @Override
    public FluidTankInfo getInfo()
    {
        return new FluidTankInfo(this);
    }

    @Override
    public int fill(FluidStack resource, boolean doFill)
    {
        if (resource == null)
        {
            return 0;
        }

        if (!doFill)
        {
            if (fluidList.isEmpty())
            {
                return Math.min(capacity, resource.amount);
            }
            return Math.min(capacity - this.getFluidAmount(), resource.amount);
        }

        
		if (fluidList.isEmpty())
        {
			FluidStack fluid = new FluidStack(resource.getFluid().getID(), Math.min(capacity, resource.amount), resource.tag);
			fluidList.add(fluid);
            return fluid.amount;
        }

		FluidStack fluid = getFluidStackWithSameFluid(resource);
		if(fluid!=null)
		{
			fluid.tag=IHLUtils.makeTagsEqual(fluid, resource);
			int amount1=Math.min(capacity - this.getFluidAmount(), resource.amount);
			fluid.amount+=amount1;
			return amount1;
		}
		fluid = new FluidStack(resource.getFluid().getID(), Math.min(capacity, resource.amount), resource.tag);
		fluidList.add(fluid);
    	this.sortFluidsByDensity();
        return fluid.amount;
    }

    @Override
    public FluidStack drain(int maxDrain, boolean doDrain)
    {
        if (fluidList.isEmpty())
        {
            return null;
        }

        int drained = maxDrain;
        
        FluidStack fluid = fluidList.get(0);
        
        if (fluid.amount < drained)
        {
            drained = fluid.amount;
        }

        FluidStack stack = new FluidStack(fluid.getFluid().getID(), drained, fluid.tag);
        if (doDrain)
        {
            fluid.amount -= drained;
            if (fluid.amount <= 0)
            {
            	this.fluidList.remove(fluid);
                fluid = null;
            }
        }
        return stack;
    }
    
    private FluidStack getFluidStackWithSameFluid(FluidStack resource)
    {
    	Iterator<FluidStack> fli = fluidList.iterator();
		while(fli.hasNext())
    	{
        	FluidStack fluid=fli.next();
        	if(fluid.isFluidEqual(resource))
        	{
        		return fluid;
        	}
    	}
		return null;
    }
    
    private FluidStack getHeaviestFluidStack()
    {
    	FluidStack fluidStack = fluidList.get(0);
    	int density=fluidStack.getFluid().getDensity();
    	Iterator<FluidStack> fli = fluidList.iterator();
		while(fli.hasNext())
    	{
        	FluidStack fluid=fli.next();
        	if(fluid.getFluid().getDensity()<density)
        	{
        		density=fluid.getFluid().getDensity();
        		fluidStack=fluid;
        	}
    	}
		return fluidStack;
    }

    /**Function replace fluid stack at position "index".
     * If there is no such index, fluidList will be filled with "fluidStack" elements. 
    **/
	public void setFluid(FluidStack fluidStack, int index) 
	{
		if(this.fluidList.size()<=index)
		{
			while(this.fluidList.size()<=index)
			{
				this.fluidList.add(fluidStack);
			}
		}
		this.fluidList.set(index, fluidStack);
	}

	public int getNumberOfFluids() 
	{
		return this.fluidList.size();
	}

	public void setFluidAmount(int amount1, int index) 
	{
		if(this.fluidList.size()<=index)
		{
			while(this.fluidList.size()<=index)
			{
				this.fluidList.add(new FluidStack(FluidRegistry.WATER,1));
			}
		}
		this.fluidList.get(index).amount=amount1;
	}
	
	public int getFluidAmount(int index) 
	{
		if(this.fluidList.size()<=index)
		{
			return 0;
		}
		return this.fluidList.get(index).amount;
	}

	public int getFluidID(int i) 
	{
		return this.fluidList.get(i).getFluid().getID();
	}
	
	public void sortFluidsByDensity()
	{
		Map<Integer, FluidStack> sortMap = new HashMap();
		int[] keysArray = new int[fluidList.size()];
    	Iterator<FluidStack> fli = fluidList.iterator();
		while(fli.hasNext())
    	{
        	FluidStack fluid=fli.next();
        	int key = fluid.getFluid().getDensity();
        	while(sortMap.containsKey(key))
        	{
        		key++;
        	}
        	sortMap.put(key, fluid);
        	keysArray[fluidList.indexOf(fluid)]=key;
    	}
		Arrays.sort(keysArray);
		ArrayList<FluidStack> newFluidList = new ArrayList<FluidStack>();
		for(int i=keysArray.length-1;i>=0;i--)
		{
			newFluidList.add(sortMap.get(keysArray[i]));
		}
		this.fluidList=newFluidList;
	}

	public FluidStack getFluid(int i) 
	{
		return this.fluidList.get(i);
	}

	public void setTag(String string, int t1_1) 
	{
		if(this.getFluid().tag==null)
		{
			this.getFluid().tag=new NBTTagCompound();
		}
		this.getFluid().tag.setInteger(string, t1_1);
	}

	public void setEmpty() 
	{
		this.fluidList.clear();
	}

	public void removeFluid(int i) 
	{
		if(this.fluidList.size()>i)
		{
			this.fluidList.remove(i);
		}
	}

	public FluidStack drain(FluidStack fluidStack, boolean doDrain) 
	{
        if (fluidList.isEmpty())
        {
            return null;
        }

        int drained = fluidStack.amount;
        
        FluidStack fluid = this.getFluidStackWithSameFluid(fluidStack);

        if (fluid==null)
        {
            return null;
        }
        
        if (fluid.amount < drained)
        {
            drained = fluid.amount;
        }

        FluidStack stack = new FluidStack(fluid.getFluid().getID(), drained, fluid.tag);
        if (doDrain)
        {
            fluid.amount -= drained;
            if (fluid.amount <= 0)
            {
            	this.fluidList.remove(fluid);
                fluid = null;
            }
        }
        return stack;
       }
}
