//extendable!
class LiquidInfo
{
	string m_LiquidClassName;
	string m_LiquidDisplayName;
	int m_LiquidType;
	float m_TemperatureLiquidFreezeThreshold = float.LOWEST;
	float m_TemperatureLiquidThawThreshold = float.LOWEST;
	float m_TemperatureLiquidBoilThreshold = Cooking.LIQUID_BOILING_POINT;
	float m_Flammability;
	
	ref NutritionalProfile m_NutriProfile;
	
	void LiquidInfo(string className, NutritionalProfile profile)
	{
		m_NutriProfile = profile;
		Init(className);
	}
	
	protected void Init(string className)
	{
		string path = "cfgLiquidDefinitions " + className;
		m_LiquidClassName = className;
		GetGame().ConfigGetTextRaw(string.Format("%1 displayName", path), m_LiquidDisplayName);
		GetGame().FormatRawConfigStringKeys(m_LiquidDisplayName);
		m_LiquidType = GetGame().ConfigGetInt(string.Format("%1 type", path));
		if (GetGame().ConfigIsExisting(string.Format("%1 liquidFreezeThreshold", path)))
			m_TemperatureLiquidFreezeThreshold = GetGame().ConfigGetFloat(string.Format("%1 liquidFreezeThreshold", path));
		if (GetGame().ConfigIsExisting(string.Format("%1 liquidThawThreshold", path)))
			m_TemperatureLiquidThawThreshold = GetGame().ConfigGetFloat(string.Format("%1 liquidThawThreshold", path));
		if (GetGame().ConfigIsExisting(string.Format("%1 liquidBoilingThreshold", path)))
			m_TemperatureLiquidBoilThreshold = GetGame().ConfigGetFloat(string.Format("%1 liquidBoilingThreshold", path));
		m_Flammability = GetGame().ConfigGetFloat(string.Format("%1 flammability", path));
	}
}

class Liquid
{
	static ref map<int, ref NutritionalProfile> m_AllLiquidsByType = new map<int, ref NutritionalProfile>; //DEPRECATED!
	static ref map<string, ref NutritionalProfile> m_AllLiquidsByName = new map<string, ref NutritionalProfile>; //DEPRECATED!
	static ref map<int, ref LiquidInfo> m_LiquidInfosByType = new map<int, ref LiquidInfo>;
	static ref map<string, ref LiquidInfo> m_LiquidInfosByName = new map<string, ref LiquidInfo>;
	static bool m_Init = InitAllLiquids();
	
	static string GetLiquidClassname(int liquid_type)
	{
		LiquidInfo info = m_LiquidInfosByType.Get(liquid_type);
		if (info)
		{
			return info.m_LiquidClassName;
		}
		
		return "";
	}
	
	static bool InitAllLiquids()
	{
		if (!g_Game)
			return false;
		
		string cfg_classname = "cfgLiquidDefinitions";
		string property_value = "NULL_PROPERTY";
		int cfg_item_count = g_Game.ConfigGetChildrenCount(cfg_classname);

		for (int i = 0; i < cfg_item_count; i++)
		{
			string liquid_class_name;
			GetGame().ConfigGetChildName(cfg_classname, i, liquid_class_name);
			string liquid_full_path = string.Format("%1 %2",cfg_classname, liquid_class_name);
			int config_liquid_type = GetGame().ConfigGetInt(string.Format("%1 type", liquid_full_path));
			
			NutritionalProfile profile = SetUpNutritionalProfile(config_liquid_type, liquid_class_name);
			LiquidInfo info = new LiquidInfo(liquid_class_name, profile);
			m_LiquidInfosByType.Insert(config_liquid_type, info);
			m_LiquidInfosByName.Insert(liquid_class_name, info);
			
			//legacy stuff
			m_AllLiquidsByType.Insert(config_liquid_type, profile);
			m_AllLiquidsByName.Insert(liquid_class_name, profile);
		}
		return true;
	}
	
	//---------------------------------------------------------------------------------------------------------
	static void Transfer(ItemBase source_ent, ItemBase target_ent, float quantity = -1)
	{
		if (!Liquid.CanTransfer(source_ent, target_ent))
			return;
		
		float source_quantity 	= source_ent.GetQuantity();
		float target_quantity 	= target_ent.GetQuantity();
		float targetCfgWeight 	= target_ent.m_ConfigWeight;
		int source_liquid_type 	= source_ent.GetLiquidType();
		
		float available_capacity = target_ent.GetQuantityMax() - target_quantity;
		float quantity_to_transfer;
		//transfers all
		if (quantity == -1)
		{
			quantity_to_transfer = Math.Clamp(source_quantity,0,available_capacity);
		}
		//transfers exact ammount
		else
		{
			quantity_to_transfer = Math.Clamp(Math.Min(source_quantity,quantity),0,available_capacity);
		}
		
		PluginTransmissionAgents m_mta = PluginTransmissionAgents.Cast(GetPlugin(PluginTransmissionAgents));
		m_mta.TransmitAgents(source_ent, target_ent, AGT_TRANSFER_COPY);
		
		source_ent.AddQuantity(-quantity_to_transfer);
		
		float retultTemp = (source_ent.GetTemperature() * quantity_to_transfer + target_ent.GetTemperature() * (targetCfgWeight + target_quantity)) / (targetCfgWeight + target_quantity + quantity_to_transfer);
		target_ent.SetTemperature(retultTemp);
		
		AffectContainerOnTransfer(target_ent,source_liquid_type,quantity_to_transfer,source_ent.GetTemperature());
		
		Liquid.FillContainer(target_ent,source_liquid_type,quantity_to_transfer);
	}
	
	static bool CanTransfer(ItemBase source_ent, ItemBase target_ent)
	{
		if (!source_ent || !target_ent)
			return false;
		
		Barrel_ColorBase barrelTarget = Barrel_ColorBase.Cast(target_ent);
		Barrel_ColorBase barrelSource = Barrel_ColorBase.Cast(source_ent);
		if ((barrelTarget && !barrelTarget.IsOpen()) || (barrelSource && !barrelSource.IsOpen()))
		{
			return false;
		}
		
		if (source_ent.GetIsFrozen())
		{
			return false;
		}
		
		float source_quantity = source_ent.GetQuantity();
		if (source_quantity <= 0)
		{
			//Debug.Log("source has no quantity", "LiquidTransfer");
			return false;//if there is nothing to transfer
		}
		
		int source_liquid_type 	= source_ent.GetLiquidType();	
		if (source_liquid_type < 1) 
		{
			//Debug.Log("source has some quantity, but does not have a valid liquidType set, liquidType = "+ToString(source_liquid_type), "LiquidTransfer");
			return false;//if source is not a container
		}
		
		if (!CanFillContainer(target_ent,source_liquid_type))
		{
			return false;
		}
		
		return true;
	}
	
	static void FillContainer(ItemBase container, int liquid_type, float amount)
	{
		if (!CanFillContainer(container,liquid_type))
		{
			return;
		}
		//filling
		container.SetLiquidType(liquid_type);
		container.AddQuantity(amount);
	}

	//! Filled from any enviro source (fuel feed, pond, snow...)
	static void FillContainerEnviro(ItemBase container, int liquid_type, float amount, bool inject_agents = true)
	{
		float containerCfgWeight = container.m_ConfigWeight;
		float retultTemp = (GetLiquidTypeEnviroTemperature(liquid_type) * amount + container.GetTemperature() * (containerCfgWeight + container.GetQuantity())) / (container.GetQuantity() + containerCfgWeight + amount);
		container.SetTemperature(retultTemp);
		AffectContainerOnFill(container,liquid_type,amount);
		
		FillContainer(container, TranslateLiquidType(liquid_type), amount);
		
		if (inject_agents)
		{
			PluginTransmissionAgents plugin = PluginTransmissionAgents.Cast(GetPlugin(PluginTransmissionAgents));
			int agtSource;
			switch (liquid_type)
			{
				case LIQUID_FRESHWATER:
				case LIQUID_RIVERWATER:
				case LIQUID_STILLWATER:
					agtSource = AGT_WATER_POND;
					break;
				case LIQUID_SNOW:
					agtSource = AGT_SNOW;
					break;
				case LIQUID_HOTWATER:
					agtSource = AGT_WATER_HOT_SPRING;
					break;
				default:
					agtSource = AGT_NONE;
					break;
			}
			
			plugin.TransmitAgents(NULL, container, agtSource, amount);
		}
	}
	
	//! from enviro source
	static void AffectContainerOnFill(ItemBase container, int liquid_type, float amount)
	{
		container.AffectLiquidContainerOnFill(liquid_type,amount);
	}
	
	static void AffectContainerOnTransfer(ItemBase container, int liquidType, float amount, float sourceLiquidTransfer)
	{
		container.AffectLiquidContainerOnTransfer(liquidType,amount,sourceLiquidTransfer);
	}
	
	static bool IsLiquidDrinkWater(int liquidType)
	{
		if (liquidType & (LIQUID_GROUP_DRINKWATER))
			return true;
		return false;
	}
	
	//! Translates 'administrative' liquid types into liquid types with valid config class
	static int TranslateLiquidType(int liquidType)
	{
		if (IsLiquidDrinkWater(liquidType))
			return LIQUID_WATER;
		else
			return liquidType;
	}
	
	static bool CanFillContainer(ItemBase container, int liquid_type, bool ignore_fullness_check = false)
	{
		if (!container) 
			return false;
		
		bool isContainerFull = container.IsFullQuantity();
		if (isContainerFull && !ignore_fullness_check)
		{
			//Debug.Log("container is full", "LiquidTransfer");
			return false;
			
		}
		
		int containerMask = container.GetLiquidContainerMask();
		if (containerMask == 0)
		{
			//Debug.Log("target is not a container", "LiquidTransfer");
			return false;//if the target liquidContainerType is set to 0
		}
		
		if ((liquid_type & containerMask) == 0)
		{
			//Debug.Log("target liquidContainerType does not support this liquid type", "LiquidTransfer");
			return false;
		}
		
		float containerQuantity = container.GetQuantity();
		
		int containerLiquidType = container.GetLiquidType();
		
		if (containerQuantity > 0 && containerLiquidType != TranslateLiquidType(liquid_type)) 
		{
			//Debug.Log("target is not empty AND is of different liquid type than liquid_type added in", "LiquidTransfer");
			return false;
		}
		return true;
	}
	
	//! Gets liquid temperature from the enviroment
	/*!
		\param[in] liquidType Type of liquid.
	*/
	static float GetLiquidTypeEnviroTemperature(int liquidType)
	{
		float ret = GetGame().GetMission().GetWorldData().GetLiquidTypeEnviroTemperature(liquidType);
		//ret = Math.Max(ret,GetLiquidFreezeThreshold(liquidType));
		
		return ret;
	}
	
	private static string GetLiquidConfigProperty(int liquid_type, string property_name, bool is_nutrition_property = false)
	{
		string cfg_classname = "cfgLiquidDefinitions";
		string property_value = "NULL_PROPERTY";
		if (!g_Game)
			return property_value;
		
		int cfg_item_count = g_Game.ConfigGetChildrenCount(cfg_classname);

		for (int i = 0; i < cfg_item_count; i++)
		{
			string liquid_class_name;
			GetGame().ConfigGetChildName(cfg_classname, i, liquid_class_name);
			string liquid_full_path = string.Format("%1 %2", cfg_classname, liquid_class_name);
			int config_liquid_type = GetGame().ConfigGetInt(string.Format("%1 type", liquid_full_path));
			
			if (config_liquid_type == liquid_type)// found the specific class, now lets extract the values
			{
				if (!is_nutrition_property) 
				{
					GetGame().ConfigGetText(string.Format("%1 %2", liquid_full_path, property_name), property_value);
					return property_value;
				}
				else
				{
					GetGame().ConfigGetText(string.Format("%1 Nutrition %2", liquid_full_path, property_name), property_value);
					return property_value;
				}
			}
		}
		return property_value;
	}
	
	static NutritionalProfile GetNutritionalProfileByType(int liquid_type)
	{
		LiquidInfo info = m_LiquidInfosByType.Get(liquid_type);
		if (info && info.m_NutriProfile)
			return info.m_NutriProfile;
		
		return null;
	}
	
	static NutritionalProfile GetNutritionalProfileByName(string class_name)
	{
		LiquidInfo info = m_LiquidInfosByName.Get(class_name);
		if (info && info.m_NutriProfile)
			return info.m_NutriProfile;
		
		return null;
	}
	
	static NutritionalProfile SetUpNutritionalProfile(int liquid_type, string liquid_class_name)
	{
		NutritionalProfile profile = new NutritionalProfile();
		profile.m_Energy = Liquid.GetLiquidConfigProperty(liquid_type, "energy", true).ToFloat();
		profile.m_NutritionalIndex = Liquid.GetLiquidConfigProperty(liquid_type, "nutritionalIndex", true).ToFloat();
		profile.m_FullnessIndex = Liquid.GetLiquidConfigProperty(liquid_type, "fullnessIndex", true).ToFloat();
		profile.m_WaterContent = Liquid.GetLiquidConfigProperty(liquid_type, "water", true).ToFloat();
		profile.m_Toxicity = Liquid.GetLiquidConfigProperty(liquid_type, "toxicity", true).ToFloat();
		profile.m_Agents = Liquid.GetLiquidConfigProperty(liquid_type, "agents", true).ToInt();
		profile.m_Digestibility = Liquid.GetLiquidConfigProperty(liquid_type, "digestibility", true).ToFloat();
		profile.m_AgentsPerDigest = Liquid.GetLiquidConfigProperty(liquid_type, "agentsPerDigest", true).ToFloat();

		profile.MarkAsLiquid(liquid_type, liquid_class_name);
		return profile;
	}
	
	static int GetAgents(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetAgents();
	}
	
	static int GetAgentsPerDigest(int liquidType)
	{
		return m_LiquidInfosByType.Get(liquidType).m_NutriProfile.m_AgentsPerDigest;
	}
	
	static float GetToxicity(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetToxicity();
	}
	
	static float GetWaterContent(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetWaterContent();
	}
	
	static float GetEnergy(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetEnergy();
	}
	
	static float GetNutritionalIndex(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetNutritionalIndex();
	}
	
	static string GetDisplayName(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_LiquidDisplayName;
	}
	
	static float GetFlammability(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_Flammability;
	}
	
	static float GetFullness(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetFullnessIndex();
	}
	
	static float GetDigestibility(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_NutriProfile.GetDigestibility();
	}
	
	static float GetFreezeThreshold(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_TemperatureLiquidFreezeThreshold;
	}
	
	static float GetThawThreshold(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_TemperatureLiquidThawThreshold;
	}
	
	static float GetBoilThreshold(int liquid_type)
	{
		return m_LiquidInfosByType.Get(liquid_type).m_TemperatureLiquidBoilThreshold;
	}
	
///////////////////////////
//deprecated methods below
///////////////////////////
	static string GetName(int liquid_type)
	{
		return Liquid.GetLiquidConfigProperty(liquid_type, "name");
	}
};