text: // Copyright 2016 Coffee Stain Studios. All Rights Reserved.
  
  #pragma once
  #include "UObject/CoreNet.h"
  #include "Array.h"
  #include "GameFramework/Actor.h"
  #include "SubclassOf.h"
  #include "UObject/Class.h"
  
  #include "FGBuildableFactory.h"
  #include "../Replication/FGReplicationDetailInventoryComponent.h"
  #include "../Replication/FGReplicationDetailActor_Manufacturing.h"
  #include "FGBuildableManufacturer.generated.h"
  
  DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FOnNewRecipeDelegate, TSubclassOf< class UFGRecipe >, newRecipe );
  
  /**
   * Base class for all buildings that are producing something out of something, i.e. constructors, smelters, refinery etc.
   */
  UCLASS( Abstract )
  class FACTORYGAME_API AFGBuildableManufacturer : public AFGBuildableFactory, public IFGRecipeProducerInterface
  {
  	GENERATED_BODY()
  public:
  	// Replication
  	virtual void GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const override;
  	virtual void PreReplication( IRepChangedPropertyTracker& ChangedPropertyTracker ) override;
  
  	/** Constructor */
  	AFGBuildableManufacturer();
  
  	// Begin AActor interface
  	virtual void BeginPlay() override; 
  	virtual void EndPlay( const EEndPlayReason::Type EndPlayReason );
  	// End AActor interface
  
  	// Begin AFGBuildableFactory interface
  	virtual bool IsConfigured() const override;
  	virtual float GetProductionCycleTime() const override;
  	virtual float GetDefaultProductionCycleTime() const override;
  	virtual float GetProductionCycleTimeForRecipe( TSubclassOf< UFGRecipe > recipe ) const override;
  	virtual float CalcProductionCycleTimeForPotential( float potential ) const override;
  	// End AFGBuildableFactory interface
  
  	// Begin IFGReplicationDetailActorOwnerInterface
  	virtual UClass* GetReplicationDetailActorClass() const override { return AFGReplicationDetailActor_Manufacturing::StaticClass(); };
  	// End IFGReplicationDetailActorOwnerInterface
  
  	/**
  	 * Move all items in the input inventory to the given inventory.
  	 * @return true if successful; false if the given inventory is full or does not support the given type of item.
  	 */
  	UFUNCTION( BlueprintCallable, Category = "FactoryGame|Factory|Inventory" )
  	virtual bool MoveOrDropInputInventory( AFGCharacterPlayer* pawn );
  
  	/**
  	 * Move all items in the input output to the given inventory.
  	 * @return true if successful; false if the given inventory is full or does not support the given type of item.
  	 */
  	UFUNCTION( BlueprintCallable, Category = "FactoryGame|Factory|Inventory" )
  	virtual bool MoveOrDropOutputInventory( AFGCharacterPlayer* pawn );
  
  	/**
  	 * Get the progress of the current production.
  	 * @return - Percent in range [0,1] of the current production progress.
  	 */	
  	virtual float GetProductionProgress() const override;
  
  	/** Checks whether or not this manufacturer contains any inputs. If false, this shouldn't contain an input component. Will be null on client. */
  	FORCEINLINE bool HasAnyInputConnetions() const { return mFactoryInputConnections.Num() > 0 || mPipeInputConnections.Num() > 0; }
  
  	/**
  	 * Gets all the recipes that can be produced.
  	 * @note This is an expensive operation so cache the result.
  	 * @param out_recipes Returns all recipes available to this machine.
  	 */
  	UFUNCTION( BlueprintCallable, Category = "FactoryGame|Factory|Recipe" )
  	void GetAvailableRecipes( TArray< TSubclassOf< class UFGRecipe > >& out_recipes ) const;
  
  	/** Returns the speed of which this building manufacture recipes */
  	UFUNCTION(BlueprintPure, Category="FactoryGame|Factory|Production")
  	FORCEINLINE float GetManufacturingSpeed() const{ return mManufacturingSpeed; }
  
  	/** Get the input inventory from this manufacturer. */
  	UFUNCTION( BlueprintPure, Category = "FactoryGame|Factory|Inventory" )
  	FORCEINLINE class UFGInventoryComponent* GetInputInventory() const { return mInputInventoryHandler->GetActiveInventoryComponent(); }
  
  	/** Get the output inventory from this manufacturer. */
  	UFUNCTION( BlueprintPure, Category = "FactoryGame|Factory|Inventory" )
  	FORCEINLINE class UFGInventoryComponent* GetOutputInventory() const { return mOutputInventoryHandler->GetActiveInventoryComponent(); }
  
  	/** Get the current recipe for manufacturing. */
  	UFUNCTION( BlueprintPure, Category = "FactoryGame|Factory|Recipe" )
  	FORCEINLINE TSubclassOf< class UFGRecipe > GetCurrentRecipe() const { return mCurrentRecipe; }
  
  	/**
  	 * Set the current recipe for manufacturing.
  	 * @note It is up to the caller to make sure input and output inventories are empty before changing recipe.
  	 *       This is so the case when players inventory is full can be handled correctly.
  	 * @param recipe - The new recipe.
  	 */
  	UFUNCTION( BlueprintCallable, Category = "FactoryGame|Factory|Recipe" )
  	void SetRecipe( TSubclassOf< class UFGRecipe > recipe );
  
  protected:
  	// Begin AFGBuildableFactory interface
  	virtual bool CanProduce_Implementation() const override;
  	virtual void Factory_CollectInput_Implementation() override;
  	virtual void Factory_PullPipeInput_Implementation( float dt ) override;
  	virtual void Factory_PushPipeOutput_Implementation( float dt ) override;
  	virtual void Factory_TickProducing( float dt ) override;
  	// End AFGBuildableFactory interface
  
  	/** Creates inventories needed for the manufacturer */
  	virtual void CreateInventories();
  
  	/** Called when NewRecipe is replicated */
  	UFUNCTION()
  	void OnRep_CurrentRecipe();
  
  	virtual void OnRep_ReplicationDetailActor() override;
  
  	/** Read the items in the input inventory. */
  	void GetInputInventoryItems( TArray< FInventoryStack >& out_items ) const;
  
  	/** Clear the items in the input inventory. */
  	void ClearInputInventoryItems();
  
  	/** Read the items in the Output inventory. */
  	void GetOutputInventoryItems( TArray< FInventoryStack >& out_items );
  
  	/** Clear the items in the Output inventory. */
  	void ClearOutputInventoryItems();
  
  	/** This function sets the filters on the input and output inventories. */
  	virtual void SetUpInventoryFilters();
  
  	/** This function assigns an inventory index to each input connection to add ingredients to
  	 *  @return - True if the ingredients did not exceed the availble connections possible. False otherwise. The Recipe should not be set if this fails.
  	 */
  	virtual bool AssignInputAccessIndices( TSubclassOf< UFGRecipe > recipe );
  
  	/** This function assigns an inventory index to each output connection to pull from */
  	virtual bool AssignOutputAccessIndices( TSubclassOf< UFGRecipe > recipe );
  
  	/** 
  	 * Helper function that actually removes ingredients from the manufacturers input. 
  	 * This can differ between buildings (e.g. Converters), so override if you need something special. 
  	 * @note this function assumes the available ingredients are there and have been checked with CanProduce()
  	 */
  	virtual void Factory_ConsumeIngredients();
  
  	/**
  	 * Returns true if the required ingredients are in the input slots.
  	 * How this is checked can differ between buildings (e.g. Converters), so override if you need something special. 
  	 */
  	virtual bool HasRequiredIngredients() const;
  
  protected:
  	friend class AFGReplicationDetailActor_Manufacturing;
  
  	//UPROPERTY( Replicated, Transient, ReplicatedUsing = OnRep_ReplicationDetailActor )
  	//class AFGReplicationDetailActor_Manufacturing* mReplicationDetailActor;
  
  	/** Called when a new recipe has been set. */
  	UPROPERTY( BlueprintAssignable, Category = "Recipe" )
  	FOnNewRecipeDelegate mCurrentRecipeChanged;
  
  	/** The speed of which this building manufacture recipes, this is a multiplier to the recipe speed */
  	UPROPERTY( EditDefaultsOnly, Category = "Production" )
  	float mManufacturingSpeed;
  
  	/** Manufacturing progress in range [0,1]. */
  	UPROPERTY( SaveGame, Meta = (NoAutoJson = true) )
  	float mCurrentManufacturingProgress;
  
  	/** Our input inventory, shared for all input connections. */
  	UPROPERTY( SaveGame )
  	class UFGInventoryComponent* mInputInventory;
  
  	/** Cached factory input connections */
  	UPROPERTY()
  	TArray<class UFGFactoryConnectionComponent*> mFactoryInputConnections;
  
  	/** Cached input pipe connections */
  	UPROPERTY()
  	TArray< class UFGPipeConnectionComponent* > mPipeInputConnections;
  
  	/** Cached factory output connections */
  	UPROPERTY()
  	TArray<class UFGFactoryConnectionComponent*> mFactoryOutputConnections;
  
  	/** Cached output pipe connections */
  	UPROPERTY()
  	TArray< class UFGPipeConnectionComponent* > mPipeOutputConnections;
  
  	/** Our output inventory, shared for all output connections. */
  	UPROPERTY( SaveGame )
  	class UFGInventoryComponent* mOutputInventory;
  
  	/** Active Inventory Component pointers which will switch once the replication detail actor has been created */
  	class UFGReplicationDetailInventoryComponent* mInputInventoryHandler;
  	class UFGReplicationDetailInventoryComponent* mOutputInventoryHandler;
  
  	/** The recipe we're currently running. */
  	UPROPERTY( SaveGame, Replicated, ReplicatedUsing = OnRep_CurrentRecipe, Meta = (NoAutoJson = true) )
  	TSubclassOf< class UFGRecipe > mCurrentRecipe;
  
  private:
  	class AFGReplicationDetailActor_Manufacturing* GetCastRepDetailsActor() const { return Cast<AFGReplicationDetailActor_Manufacturing>( mReplicationDetailActor ); };
  
  public:
  	FORCEINLINE ~AFGBuildableManufacturer() = default;
  };
  