class Fireplace extends FireplaceBase
{
	bool m_ContactEventProcessing = false;
	protected bool m_ContactDeletionProcessing = false;
	
	void Fireplace()
	{
		//Particles - default for FireplaceBase
		PARTICLE_FIRE_START 	= ParticleList.CAMP_FIRE_START;
		PARTICLE_SMALL_FIRE 	= ParticleList.CAMP_SMALL_FIRE;
		PARTICLE_NORMAL_FIRE	= ParticleList.CAMP_NORMAL_FIRE;
		PARTICLE_SMALL_SMOKE 	= ParticleList.CAMP_SMALL_SMOKE;
		PARTICLE_NORMAL_SMOKE	= ParticleList.CAMP_NORMAL_SMOKE;
		PARTICLE_FIRE_END 		= ParticleList.CAMP_FIRE_END;
		PARTICLE_STEAM_END		= ParticleList.CAMP_STEAM_2END;
		
		SetEventMask( EntityEvent.CONTACT | EntityEvent.TOUCH );

		//! universal temperature sources overrides
		m_UTSSettings.m_TemperatureItemCap	= GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE;
		m_UTSSettings.m_TemperatureCap		= 20;
		
		m_ThawnSurfaceUnderSupport = true;
	}
	
	override protected void InitializeTemperatureSources()
	{
		m_UTSLFireplace = new UniversalTemperatureSourceLambdaFireplace();
		m_UTSLFireplace.SetSmallFireplaceTemperatureMax(PARAM_SMALL_FIRE_TEMPERATURE);
		m_UTSLFireplace.SetNormalFireplaceTemperatureMax(PARAM_OUTDOOR_FIRE_TEMPERATURE);

		m_UTSource = new UniversalTemperatureSource(this, m_UTSSettings, m_UTSLFireplace);
	}
	
	override bool IsBaseFireplace()
	{
		return true;
	}
	
	override bool CanObstruct()
	{
		return IsOven();
	}
	
	override bool CanProxyObstruct()
	{
		return IsOven();
	}
	
	override void EOnTouch( IEntity other, int extra )
	{
		ContactEvent( other, GetPosition() );
	}
	
	override void EOnContact( IEntity other, Contact extra )
	{
		ContactEvent( other, extra.Position );
	}
	
	void ContactEvent( IEntity other, vector position )
	{		
		if (GetGame().IsServer() && !m_ContactEventProcessing && dBodyIsActive(this) && !IsSetForDeletion())
			m_ContactEventProcessing = true;
	}
	
	override protected void EOnPostSimulate(IEntity other, float timeSlice)
	{
		super.EOnPostSimulate(other,timeSlice);
		
		if (m_ContactEventProcessing && !m_ContactDeletionProcessing)
		{
			MiscGameplayFunctions.ThrowAllItemsInInventory(this, 0);
			CheckForDestroy();
			m_ContactDeletionProcessing = true;
		}
	}
	
	//attachments
	override bool CanReceiveAttachment( EntityAI attachment, int slotId )
	{
		if ( !super.CanReceiveAttachment(attachment, slotId) )
			return false;

		ItemBase item = ItemBase.Cast( attachment );

		//cookware
		if ( ( item.Type() == ATTACHMENT_CAULDRON ) || ( item.Type() == ATTACHMENT_COOKING_POT ) )
		{
			if ( FindAttachmentBySlotName( "CookingTripod" ) || IsOven() ) 
				return true;
			return false;
		}
		if ( item.Type() == ATTACHMENT_FRYING_PAN )
		{
			if ( IsOven() ) 
				return true;
			return false;
		}

		// food on direct cooking slots
		if ( item.IsKindOf( "Edible_Base" ) )
		{
			if ( IsOven() )
				return true;
			return false;
		}
		//tripod
		
		if ( item.IsInherited(TripodBase) )
		{
			if (!IsOven() && !GetHierarchyParent())
				return true;
			return false;
		}
		
		//stones
		if ( item.Type() == ATTACHMENT_STONES )
		{
			if ( !GetHierarchyParent() && !IsBurning() )
				return true;
			return false;
		}
		
		return true;
	}
	
	override bool CanDisplayAttachmentSlot( int slot_id )
	{
		string slot_name = InventorySlots.GetSlotName(slot_id);
		if ( super.CanDisplayAttachmentSlot(slot_id) )
		{
			if ( slot_name != "CookingEquipment" || FindAttachmentBySlotName( "CookingTripod" ) )
				return true;
		}
		
		return false;
	}
	
	override bool CanLoadAttachment( EntityAI attachment )
	{
		if ( !super.CanLoadAttachment(attachment) )
			return false;

		ItemBase item = ItemBase.Cast( attachment );

		//cookware
		if ( ( item.Type() == ATTACHMENT_CAULDRON ) || ( item.Type() == ATTACHMENT_COOKING_POT ) )
		{
			//if ( IsItemTypeAttached( ATTACHMENT_TRIPOD ) /*|| IsOven()*/ ) 
				return true;
		}
		if ( item.Type() == ATTACHMENT_FRYING_PAN )
		{
			//if ( IsOven() ) 
				return true;
		}

		// food on direct cooking slots
		//if ( IsOven() )
		//{
			if ( item.IsKindOf( "Edible_Base" ) )
				return true;
		//}
		//tripod
		if ( item.Type() == ATTACHMENT_TRIPOD )
		{
			if ( /*!IsOven() &&*/ GetHierarchyParent() == NULL )
				return true;
		}
		
		//stones
		if ( item.Type() == ATTACHMENT_STONES )
		{
			if ( GetHierarchyParent() /*|| IsBurning()*/ )
				return false;
			
			return true;
		}
		
		return true;
	}

	override bool CanReleaseAttachment( EntityAI attachment )
	{
		if (!super.CanReleaseAttachment(attachment))
		{
			return false;
		}
		
		ItemBase item = ItemBase.Cast(attachment);
		//stones
		if (item.Type() == ATTACHMENT_STONES)
		{
			if (IsBurning())
			{
				return false;
			}

			int stone_quantity = item.GetQuantity();
			if (HasStoneCircle() && stone_quantity <= 8)
			{
				return false;
			}

			if (IsOven())
			{
				return false;
			}
		}
		
		return true;
	}

	override void EEItemAttached(EntityAI item, string slot_name)
	{
		super.EEItemAttached(item, slot_name);
		
		ItemBase item_base = ItemBase.Cast(item);
		
		if (IsKindling(item_base) || IsFuel(item_base))
		{
			AddToFireConsumables(item_base);
		}
		
		//cookware
		if (item_base.IsCookware())
			SetCookingEquipment(item_base);
		
		if (GetGame().IsServer() && !IsOven())
		{
			if (item_base.Type() == ATTACHMENT_COOKING_POT)
				item_base.SetAnimationPhase(ANIMATION_COOKWARE_HANDLE, 0);
			if (item.Type() == ATTACHMENT_CAULDRON)
				item_base.SetAnimationPhase(ANIMATION_CAULDRON_HANDLE, 0);
		}
		
		// direct cooking/smoking slots
		bool edible_base_attached = false;
		switch (slot_name)
		{
		case "DirectCookingA":
			m_DirectCookingSlots[0] = item_base;
			edible_base_attached = true;
		break;
		case "DirectCookingB":
			m_DirectCookingSlots[1] = item_base;
			edible_base_attached = true;
		break;
		case "DirectCookingC":
			m_DirectCookingSlots[2] = item_base;
			edible_base_attached = true;
		break;

		case "SmokingA":
			m_SmokingSlots[0] = item_base;
			edible_base_attached = true;
		break;
		case "SmokingB":
			m_SmokingSlots[1] = item_base;
			edible_base_attached = true;
		break;
		case "SmokingC":
			m_SmokingSlots[2] = item_base;
			edible_base_attached = true;
		break;
		case "SmokingD":
			m_SmokingSlots[3] = item_base;
			edible_base_attached = true;
		break;
		}
		
		//TODO
		//add SetViewIndex when attaching various attachments
		
		RefreshFireplaceVisuals();
	}

	override void EEItemDetached(EntityAI item, string slot_name)
	{
		super.EEItemDetached(item, slot_name);
		
		ItemBase item_base = ItemBase.Cast(item);
		
		if (IsKindling(item_base) || IsFuel(item_base))
		{
			RemoveFromFireConsumables(GetFireConsumableByItem(item_base));
		}

		//cookware
		if (item_base.IsCookware())
		{
			ClearCookingEquipment(item_base);
			item_base.RemoveAudioVisualsOnClient();
		}
		
		if (item_base.IsLiquidContainer()) //boiling bottle effects stop
			item_base.RemoveAudioVisualsOnClient();
		
		if (GetGame().IsServer())
		{
			if (item_base.Type() == ATTACHMENT_COOKING_POT)
				item_base.SetAnimationPhase(ANIMATION_COOKWARE_HANDLE, 1);
			if (item_base.Type() == ATTACHMENT_CAULDRON)
				item_base.SetAnimationPhase(ANIMATION_CAULDRON_HANDLE, 1);
		}
		
		// direct cooking/smoking slots
		switch (slot_name)
		{
		case "DirectCookingA":
			m_DirectCookingSlots[0] = null;
		break;
		case "DirectCookingB":
			m_DirectCookingSlots[1] = null;
		break;
		case "DirectCookingC":
			m_DirectCookingSlots[2] = null;
		break;

		case "SmokingA":
			m_SmokingSlots[0] = null;
		break;
		case "SmokingB":
			m_SmokingSlots[1] = null;
		break;
		case "SmokingC":
			m_SmokingSlots[2] = null;
		break;
		case "SmokingD":
			m_SmokingSlots[3] = null;
		break;
		}
		
		//no attachments left, no cargo items & no ashes are present
		CheckForDestroy();
		RefreshFireplaceVisuals();
	}
	
	
	override void SetCookingEquipment(ItemBase equipment)
	{
		super.SetCookingEquipment(equipment);
		
		TripodBase stand = TripodBase.Cast(FindAttachmentBySlotName("CookingTripod"));
		if (stand)
		{
			if (equipment)
				stand.LockToParent();
			else
				stand.UnlockFromParent();
		}
	}
	
	override void OnBeforeTryDelete() 
	{
		super.OnBeforeTryDelete();

		MiscGameplayFunctions.DropAllItemsInInventoryInBounds(this, m_HalfExtents);
	}

	override bool IsPrepareToDelete()
	{
		return GetInventory().AttachmentCount() == 0 && !IsBurning() && !HasAshes();
	}
	
	//CONDITIONS
	//this into/outo parent.Cargo
	override bool CanPutInCargo(EntityAI parent)
	{
		if (!super.CanPutInCargo(parent))
		{
			return false;
		}
		
		if (HasAshes() || IsBurning() || HasStones() || HasStoneCircle() || IsOven() || !IsCargoEmpty() || HasCookingStand())
		{
			return false;
		}
		
		return true;
	}

	override bool CanRemoveFromCargo( EntityAI parent )
	{
		return true;
	}
	
	//cargo item into/outo this.Cargo
	override bool CanReceiveItemIntoCargo( EntityAI item )
	{
		if ( GetHierarchyParent() )
			return false;
		
		return super.CanReceiveItemIntoCargo( item );
	}
	
	override bool CanLoadItemIntoCargo( EntityAI item )
	{
		if (!super.CanLoadItemIntoCargo( item ))
			return false;
		
		if ( GetHierarchyParent() )
			return false;
		
		return true;
	}
	
	//hands
	override bool CanPutIntoHands(EntityAI parent)
	{
		if (!super.CanPutIntoHands(parent))
		{
			return false;
		}
		
		if (HasAshes() || IsBurning() || HasStones() || HasStoneCircle() || IsOven() || !IsCargoEmpty() || HasCookingStand())
		{
			return false;
		}
		
		return true;
	}
	
	override bool CanDisplayAttachmentCategory( string category_name )
	{
		if ( !super.CanDisplayAttachmentCategory( category_name ) )
			return false;
		
		if ( IsOven() )
		{
			if ( category_name == "CookingEquipment" )
				return false;
			if ( ( category_name == "DirectCooking" ) || ( category_name == "Smoking" ) )
				return true;
		}
		else
		{
			if ( category_name == "CookingEquipment" )
				return true;
			if ( ( category_name == "DirectCooking" ) || ( category_name == "Smoking" ) )
				return false;
		}
		return true;
	}
	
	override bool CanAssignAttachmentsToQuickbar()
	{
		return false;
	}
	
	override float HeightStartCheckOverride()
	{
		return 0.5;
	}
	
	//particles
	override bool CanShowSmoke()
	{
		return !IsOven();
	}
	
	
	void DestroyClutterCutter( Object clutter_cutter )
	{
		GetGame().ObjectDelete( clutter_cutter );
	}
	
	override void RefreshPhysics()
	{
		super.RefreshPhysics();
		
		//Oven
		if ( IsOven() )
		{
			RemoveProxyPhysics( ANIMATION_OVEN );
			AddProxyPhysics( ANIMATION_OVEN );
		}
		else
		{
			RemoveProxyPhysics( ANIMATION_OVEN );
		}
		
		//Tripod
		if ( IsItemTypeAttached( ATTACHMENT_TRIPOD ) )
		{
			RemoveProxyPhysics( ANIMATION_TRIPOD );
			AddProxyPhysics( ANIMATION_TRIPOD );
		}
		else
		{
			RemoveProxyPhysics( ANIMATION_TRIPOD );
		}	
	}
	
	override void RefreshFireplacePhysics()
	{
		RefreshPhysics();
	}

	//on store save/load
	override void OnStoreSave( ParamsWriteContext ctx )
	{   
		super.OnStoreSave(ctx);
		
		if ( GetGame().SaveVersion() >= 110 )
		{
			// save stone circle state
			ctx.Write( m_HasStoneCircle );
			
			// save stone oven state
			ctx.Write( m_IsOven );
		}
	}
	
	override bool OnStoreLoad( ParamsReadContext ctx, int version )
	{
		if ( !super.OnStoreLoad(ctx, version) )
			return false;

		if ( version >= 110 )
		{
			// read stone circle state
			if ( !ctx.Read( m_HasStoneCircle ) )
			{
				m_HasStoneCircle = false;
				return false;
			}
			// read stone oven state
			if ( !ctx.Read( m_IsOven ) )
			{
				m_IsOven = false;
				return false;
			}
		}		
		return true;
	}
	
	override void AfterStoreLoad()
	{	
		super.AfterStoreLoad();
		
		if ( IsBurning() )
		{
			if ( !m_ClutterCutter )
			{
				m_ClutterCutter = GetGame().CreateObjectEx( OBJECT_CLUTTER_CUTTER, GetPosition(), ECE_PLACE_ON_SURFACE );
				m_ClutterCutter.SetOrientation( GetOrientation() );
			}
		}
	}
	
	//================================================================
	// IGNITION ACTION
	//================================================================	
	
	// Item-to-item fire distribution
	override bool HasFlammableMaterial()
	{
		return true;
	}
	
	override bool CanBeIgnitedBy(EntityAI igniter = NULL)
	{
		return HasAnyKindling() && !IsBurning() && !GetHierarchyParent();
	}
	
	override bool CanIgniteItem(EntityAI ignite_target = NULL)
	{
		return IsBurning();
	}
	
	override bool IsIgnited()
	{
		return IsBurning();
	}
	
	override void OnIgnitedTarget( EntityAI target_item )
	{
	}
	
	override void OnIgnitedThis( EntityAI fire_source )
	{	
		//remove grass
		Object cc_object = GetGame().CreateObjectEx( OBJECT_CLUTTER_CUTTER , GetPosition(), ECE_PLACE_ON_SURFACE );
		cc_object.SetOrientation ( GetOrientation() );
		GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( DestroyClutterCutter, 200, false, cc_object );
		
		//start fire
		StartFire();
		
		if ( fire_source )
		{
			Man player = fire_source.GetHierarchyRootPlayer();
			
			if ( player )
			{
				EFireIgniteType ignate_type = EFireIgniteType.Unknow;
				
				if ( fire_source.ClassName() == "Matchbox"  )
				{
					ignate_type = EFireIgniteType.Matchbox;
				}
				else if ( fire_source.ClassName() == "Roadflare"  )
				{
					ignate_type = EFireIgniteType.Roadflare;
				}
				else if ( fire_source.ClassName() == "HandDrillKit"  )
				{
					ignate_type = EFireIgniteType.HandDrill;
				}
				
				
				SyncEvents.SendPlayerIgnatedFireplace( player, ignate_type );
			}
		}
	}

	override bool IsThisIgnitionSuccessful( EntityAI item_source = NULL )
	{
		SetIgniteFailure( false );
		Param1<bool> failure;
		
		//check kindling
		if ( !HasAnyKindling() )
		{
			return false;
		}
		
		//check roof
		if ( !IsOven() )
		{
			if ( !IsCeilingHighEnoughForSmoke() && IsOnInteriorSurface() )
			{
				return false;
			}
		}
		
		//check surface
		if ( IsOnWaterSurface() )
		{
			return false;
		}

		//check wetness
		if ( IsWet() )
		{
			SetIgniteFailure( true );
			
			failure = new Param1<bool>( GetIgniteFailure() );
			GetGame().RPCSingleParam( this, FirePlaceFailure.WET, failure, true );
			return false;
		}

		// check if the fireplace isnt below a roof
		//  excluding this check whein oven stage
		CheckForRoofLimited(0);
		if ( !IsOven() && !IsRoofAbove() )
		{
			// if not, check if there is strong rain or wind
			if ( IsRainingAbove() )
			{
				SetIgniteFailure( true );
				
				failure = new Param1<bool>( GetIgniteFailure() );
				GetGame().RPCSingleParam( this, FirePlaceFailure.WET, failure, true );
				return false;
			}
			
			if ( IsWindy() )
			{
				SetIgniteFailure( true );
				
				failure = new Param1<bool>( GetIgniteFailure() );
				GetGame().RPCSingleParam( this, FirePlaceFailure.WIND, failure, true );
				return false;
			}
		}
		
		return true;	
	}
	
	//================================================================
	// FIREPLACE ENTITY
	//================================================================	
	static Fireplace IgniteEntityAsFireplace( notnull EntityAI entity, notnull EntityAI fire_source )
	{
		//get player
		PlayerBase player = PlayerBase.Cast( fire_source.GetHierarchyRootPlayer() );
		
		//create fireplace
		Fireplace fireplace = Fireplace.Cast( GetGame().CreateObjectEx( "Fireplace" , entity.GetPosition(), ECE_PLACE_ON_SURFACE ) );
		
		//attach
		if ( !GetGame().IsMultiplayer() )		//clear inventory reservation (single player)
		{
			InventoryLocation loc = new InventoryLocation;
			entity.GetInventory().GetCurrentInventoryLocation( loc );
			player.GetInventory().ClearInventoryReservationEx( entity, loc );
		}
		
		if ( GetGame().IsServer() && GetGame().IsMultiplayer() )
		{
			player.ServerTakeEntityToTargetAttachment( fireplace, entity ); // multiplayer server side
		}
		else
		{
			player.LocalTakeEntityToTargetAttachment( fireplace, entity ); // single player or multiplayer client side
		}
		
		//start fire
		fireplace.StartFire();
		fireplace.OnIgnitedThis(fire_source);
		
		return fireplace;
	}
	
	static bool CanIgniteEntityAsFireplace(notnull EntityAI entity)
	{
		//check surface
		if (FireplaceBase.IsEntityOnWaterSurface(entity))
		{
			return false;
		}
		
		entity.CheckForRoofLimited(); //TODO: limit more severely? Should update at least once during UATimeSpent.FIREPLACE_IGNITE 
		if (!entity.IsRoofAbove())
			return !FireplaceBase.IsRainingAboveEntity(entity);
		
		//check ceiling (enough space for smoke)
		if (IsEntityOnInteriorSurface(entity) && MiscGameplayFunctions.IsUnderRoof(entity, FireplaceBase.MIN_CEILING_HEIGHT))
		{
			return false;
		}
		
		return true;
	}
	
	//================================================================
	// ADVANCED PLACEMENT
	//================================================================
		
	override void SetActions()
	{
		super.SetActions();
		
		AddAction(ActionPlaceFireplaceIntoBarrel);
		AddAction(ActionPlaceFireplaceIndoor);
		AddAction(ActionPlaceOvenIndoor);
		AddAction(ActionTogglePlaceObject);
		AddAction(ActionPlaceObject);
		AddAction(ActionBuildOven);
		AddAction(ActionDismantleOven);
		AddAction(ActionBuildStoneCircle);
		AddAction(ActionDismantleStoneCircle);
	}
}
