private PropertyOwnership AddProperty(Property property, float value = 0)
        {
            PropertyOwnership newPropertyOwnership = new PropertyOwnership(property, value, owner);

            propertyOwnerships.Add(newPropertyOwnership);
            return(newPropertyOwnership);
        }
        private bool RemoveProperty(PropertyOwnership propertyOwnership)
        {
            bool found = propertyOwnerships.Remove(propertyOwnership);

            if (found)
            {
                Debug.Log($"Removing property ownership ({propertyOwnership.ToString()}) from {owner}");
                return(true);
            }
            Debug.LogWarning($"Trying to remove a property ({propertyOwnership.ToString()}) from '{owner}' that doesn't exist.");
            return(false);
        }
        /// <summary>
        /// Looks for an ExecutionPlan to cover the Property in the given PropertyOwnership relation using the Properties this PropertyManager.
        /// </summary>
        /// <param name="propertyOwnershipToCover">The PropertyOwnership to which it is wanted to increase the value.</param>
        /// <param name="remainingValueToCover">The amount of value that is desired to increase with the obtained ExecutionPlan</param>
        /// <param name="executer">The MapElement that will execute the ExecutionPlan</param>
        /// <returns></returns>
        public ExecutionPlan GetExecutionPlanToCover(PropertyOwnership propertyOwnershipToCover, float remainingValueToCover, MapElement executer)
        {
            MapElement target = propertyOwnershipToCover.owner;

            // Debug.Log($" >>> Searching for an execution plan to cover '{remainingValueToCover}' of '{propertyOwnershipToCover.property}' owned by '{propertyOwnershipToCover.owner}' executed by '{executer}'.\n");

            foreach (PropertyOwnership ownedProperty in propertyOwnerships)
            {
                foreach (MapEvent mapEvent in ownedProperty.property.mapEvents)
                {
                    // Debug.Log($" >>> In '{ownedProperty.owner}', checking if mapEvent '{mapEvent}' in property '{ownedProperty.property}' can cover {remainingValueToCover} missing of the property '{propertyOwnershipToCover.property}'.\nTarget: {target}, Executer: {executer}, EventOwner still unknown.\n");
                    if (!mapEvent.executerMustOwnProperty || (mapEvent.executerMustOwnProperty && ownedProperty.owner == executer))
                    {
                        MapElement    eventOwner    = ownedProperty.owner;
                        ExecutionPlan executionPlan = new ExecutionPlan(mapEvent, executer, target, eventOwner, ownedProperty.property);
                        executionPlan.SetExecutionTimesToCover(propertyOwnershipToCover, remainingValueToCover);
                        int executionsToCover = executionPlan.executionTimes;
                        if (executionsToCover < 0) // The executionPlan can not cover the property
                        {
                            continue;
                        }

                        Dictionary <PropertyOwnership, float> mapEventRequirementsNotMet = mapEvent.GetRequirementsNotMet(ownedProperty.property, executer, target, owner, executionPlan.executionTimes);

                        if (mapEvent.tryToCoverRequirementsIfNotMet || (!mapEvent.tryToCoverRequirementsIfNotMet && mapEventRequirementsNotMet.IsNullOrEmpty()))
                        {
                            // If reached here, the mapEvent can be executed - Now choose if it is the appropriate one
                            // Debug.Log($"   > The mapEvent '{mapEvent}' can be executed ({mapEventRequirementsNotMet.Count} requirements must be covered before):\n    - {mapEventRequirementsNotMet.ToStringAllElements("\n    - ")}\n");

                            if (mapEvent.ConsequencesCover(propertyOwnershipToCover, target, executer, eventOwner, ownedProperty.property))
                            {
                                Debug.Log($" ● Found Execution Plan: {executionPlan}\n");
                                return(executionPlan);
                            }
                        }
                    }
                    else
                    {
                        // Debug.Log($"    The executer ({executer}) must own the property '{currentOwnedProperty.property}' to execute '{mapEvent}' but it does not. MapEvent owned by '{currentOwnedProperty.ownerMapElement}'.\n");
                        if (mapEvent.ConsequencesCover(propertyOwnershipToCover, target, executer, executer, ownedProperty.property))
                        {
                            ExecutionPlan executionPlan = new ExecutionPlan(mapEvent, executer, target, executer, ownedProperty.property);
                            executionPlan.SetExecutionTimesToCover(propertyOwnershipToCover, remainingValueToCover);
                            Debug.Log($" ● Found 'forced' Execution Plan: {executionPlan}\n");
                            return(executionPlan);
                        }
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// Updates the value of the PropertyOwnerships with the given Property. If no PropertyOwnerships related to the given Property is found, a new PropertyOwnerships will be created in this PropertyManager with the given value.
        /// </summary>
        /// <param name="propertyToUpdate">The Property that the updated PropertyOwnership must have.</param>
        /// <param name="deltaValue">The difference that is wanted to apply to the current value of the PropertyOwnership. Can be positive and negative.</param>
        public void UpdateProperty(Property propertyToUpdate, float deltaValue)
        {
            if (propertyToUpdate == null)
            {
                return;
            }

            PropertyOwnership foundPropertyOwnership = null;

            foreach (PropertyOwnership propertyOwnership in propertyOwnerships)
            {
                if (propertyOwnership.property != propertyToUpdate)
                {
                    continue;
                }

                foundPropertyOwnership = propertyOwnership;
                foundPropertyOwnership.UpdateValue(deltaValue);
                //Debug.Log($"         > The new value for the property '{managerProperty}' in '{ownerMapElement}' is = {managerProperty.value}");
            }

            if (foundPropertyOwnership == null)
            {
                AddProperty(propertyToUpdate, deltaValue); // So, if the update was to add a new property
            }
            else
            {
                if (foundPropertyOwnership.value == 0)
                {
                    if (foundPropertyOwnership.property == null)
                    {
                        Debug.LogError($"NULL PROPERTY IN {foundPropertyOwnership}. Error happened while trying to update the property {propertyToUpdate} with a delta value of {deltaValue}.");
                    }
                    else
                    {
                        switch (foundPropertyOwnership.property.behaviourWhenEmpty)
                        {
                        case BehaviourWhenEmpty.Remove:
                            RemoveProperty(foundPropertyOwnership);
                            break;

                        case BehaviourWhenEmpty.TakeCare:     // The care should be taken somewhere else
                            break;

                        case BehaviourWhenEmpty.DoNothing:
                            break;

                        case BehaviourWhenEmpty.Transform:
                            foreach (PropertyOwnership inheritanceProperty in foundPropertyOwnership.property.inheritanceProperties)
                            {
                                AddProperty(inheritanceProperty.property, inheritanceProperty.value);
                            }
                            RemoveProperty(foundPropertyOwnership);
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            }
        }
        public float GetValueOf(Property property)
        {
            PropertyOwnership foundProperty = GetOwnedPropertyAndAddItIfNotFound(property);

            return(foundProperty.value);
        }