/**
	Liquid Container
	Basic interface for anything that can contain liquids.
	
	@author Marky
*/


public func IsLiquidContainer() { return true; }

public func GetLiquidContainerMaxFillLevel(liquid_name)
{
	return 0;
}

public func IsLiquidContainerForMaterial(string liquid_name)
{
	return true;
}

public func GetLiquidAmount(liquid_name)
{
	var amount = 0;
	var type = nil;
	
	// In case that a value was supplied, try finding the type for that.
	if (liquid_name != nil)
	{
		type = GetLiquidDef(liquid_name);
		if (type == nil)
			FatalError(Format("GetLiquidAmount(%s): No such liquid.", liquid_name));
	}

	// Return everything if 'nil' was passed, or a specific amount if a value was passed.
	for (var liquid in GetLiquidContents())
	{
		if (liquid_name == nil || liquid->GetLiquidType() == type->GetLiquidType())
			amount += liquid->~GetLiquidAmount();
	}
	return amount;
}

public func GetLiquidAmountRemaining(liquid_name)
{
	return GetLiquidContainerMaxFillLevel(liquid_name) - GetLiquidAmount(liquid_name);
}

public func GetLiquidContents()
{
	return FindObjects(Find_Container(this), Find_Func("IsLiquid"));
}


/*-- Interaction --*/

// Returns whether this container has any of the requested liquid and returns that liquid.
// If liquids == nil it returns the first liquid found.
public func HasLiquid(array liquids)
{
	for (var liquid in GetLiquidContents())
	{
		if (liquids != nil)
		{
			if (IsValueInArray(liquids, liquid->GetID()))
				return liquid;
		}
		else
			return liquid;
	}
	return nil;
}


/**
Extracts liquid from the container.
@param liquid_name: Material to extract; Wildcardsupport
               Defaults to the current liquid if 'nil' is passed.
@param amount: Max Amount of liquid being extracted;
               Defaults to all contained liquid if 'nil' is passed.
@param destination: Object that extracts the liquid
@return [returned_liquid, returned_amount]
	   - returned_liquid: Material being extracted
	   - returned_amount: Amount being extracted
*/
public func RemoveLiquid(liquid_name, int amount, object destination)
{
	if (amount < 0)
	{
		FatalError(Format("You can remove positive amounts of liquid only, got %d", amount));
	}

	amount = amount ?? GetLiquidAmount();
	
	var removed = 0;
	for (var liquid in GetLiquidContents())
	{
		if (removed >= amount) break;
	
		if (!liquid_name) liquid_name = liquid->GetLiquidType();
		
		removed += liquid->RemoveLiquid(liquid_name, amount - removed, destination)[1];
	}

	return [liquid_name, removed];
}


/** 
Inserts liquid into the container.
@param liquid_name: Material to insert
@param amount: Max amount of material being inserted.
               Passing a nil parameter will fill the
               container to its maximum.
@param source: Object which inserts the liquid [optional]
@return returned_amount: The inserted amount
*/
public func PutLiquid(liquid_name, int amount, object source)
{
	amount = amount ?? this->GetLiquidAmountRemaining();

	if (amount < 0)
	{
		FatalError(Format("You can insert positive amounts of liquid only, got %d", amount));
	}

	var type = GetLiquidDef(liquid_name);
	
	var max = this->GetLiquidContainerMaxFillLevel(liquid_name);
	var before = GetLiquidAmount(liquid_name);
	if (max > 0 && before >= max) return 0;

	if (type)
	{
		var liquid = type->~CreateLiquid(amount);
		if (liquid) Collect(liquid, true);
		// the check is necessary here, because the liquid may get removed if the barrel already
		// has a stack inside
		if (liquid && !(liquid->Contained())) liquid->RemoveObject();
	}

	var after = GetLiquidAmount(liquid_name);
	return after - before;
}

public func AcceptsLiquid(liquid_name, int amount)
{
	// Container must accept material and not be full.
	var liquid_type = GetLiquidDef(liquid_name)->GetLiquidType();
	return IsLiquidContainerForMaterial(liquid_type) && amount <= this->GetLiquidContainerMaxFillLevel(liquid_name) - GetLiquidAmount(liquid_name);
}

private func GetLiquidDef(liquid_name)
{
	if (GetType(liquid_name) == C4V_String)
		return GetDefinition(liquid_name);
	else if (GetType(liquid_name) == C4V_Def)
		return liquid_name;
	FatalError(Format("The first parameter of GetLiquidDef() must either be a string or definition. You passed %v.", liquid_name));
	return nil;
}