/// <param name="accessor"></param>
    /// <param name="item">The item to transfer</param>
    /// <param name="source">The source inventory</param>
    /// <param name="destination">The destination inventory</param>
    /// <param name="stacks">How many stacks to move. Use -1 to specify 'all' stacks</param>
    /// <param name="destinationCapacity">The capacity of the destination inventory</param>
    private static ItemTransactionResult ExecuteItemTransaction_Internal(ISimGameWorldReadWriteAccessor accessor, ref ItemTransationData data)
    {
        if (data.Stacks == 0)
        {
            return(new ItemTransactionResult(stackTransfered: 0));
        }

        if (data.Stacks < 0) // invert source and destination ?
        {
            var temp1 = data.SourceBuffer;
            var temp2 = data.SourceCapacity;
            data.SourceBuffer        = data.DestinationBuffer;
            data.SourceCapacity      = data.DestinationCapacity;
            data.DestinationBuffer   = temp1;
            data.DestinationCapacity = temp2;
            data.Stacks = -data.Stacks;
        }

        if (!accessor.Exists(data.Item))
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_BadTransationRequest));
        }

        if (data.SourceBuffer != null && !data.SourceBuffer.Value.IsCreated)
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_SourceInvalid));
        }

        if (data.DestinationBuffer != null && !data.DestinationBuffer.Value.IsCreated)
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_DestinationInvalid));
        }

        if (!accessor.HasComponent <SimAssetId>(data.Item))
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_ItemHasNoSimAssetId));
        }

        var itemAssetId       = accessor.GetComponent <SimAssetId>(data.Item);
        var sourceIndex       = -1;
        var destinationIndex  = -1;
        var itemStackable     = accessor.GetComponent <StackableFlag>(data.Item);
        var sourceBuffer      = data.SourceBuffer.GetValueOrDefault();
        var destinationBuffer = data.DestinationBuffer.GetValueOrDefault();

        if (sourceBuffer.IsCreated) // Find item in source
        {
            for (int i = 0; i < sourceBuffer.Length; i++)
            {
                if (accessor.TryGetComponent(sourceBuffer[i].ItemEntity, out SimAssetId assetId) && assetId == itemAssetId)
                {
                    sourceIndex = i;
                    break;
                }
            }
        }

        if (destinationBuffer.IsCreated) // Find item in destination
        {
            for (int i = 0; i < destinationBuffer.Length; i++)
            {
                if (accessor.TryGetComponent(destinationBuffer[i].ItemEntity, out SimAssetId assetId) && assetId == itemAssetId)
                {
                    destinationIndex = i;
                    break;
                }
            }
        }

        if (sourceBuffer.IsCreated && sourceIndex == -1)
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_ItemNotFoundInSource));
        }

        if (destinationBuffer.IsCreated && destinationIndex == -1 && destinationBuffer.Length >= data.DestinationCapacity)
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_DestinationFull));
        }

        // cap stacks
        if (sourceIndex >= 0)
        {
            data.Stacks = min(data.Stacks, sourceBuffer[sourceIndex].Stacks);
            if (data.Stacks == 0)
            {
                return(new ItemTransactionResult(ItemTransactionResultType.Failed_SourceStack0));
            }
        }

        if (!itemStackable)
        {
            data.Stacks = 1;
        }

        // check unique item is not about to be added twice
        if (!itemStackable && destinationIndex != -1 && destinationBuffer[destinationIndex].Stacks > 0)
        {
            return(new ItemTransactionResult(ItemTransactionResultType.Failed_UniqueItemAlreadyInDestination));
        }

        ////////////////////////////////////////////////////////////////////////////////////////
        //      Perform Transaction
        ////////////////////////////////////////////////////////////////////////////////////////

        // Remove from source
        if (sourceIndex != -1)
        {
            // decrease stacks
            var sourceEntry = sourceBuffer[sourceIndex];
            sourceEntry.Stacks       -= data.Stacks;
            sourceBuffer[sourceIndex] = sourceEntry;

            if (sourceEntry.Stacks <= 0) // remove from source if 0 left
            {
                // remove from source
                sourceBuffer.RemoveAt(sourceIndex);

                // Destroy item
                accessor.DestroyEntity(sourceEntry.ItemEntity);
                UpdateBuffersAfterStructuraleChange(ref data);
            }
        }

        if (destinationBuffer.IsCreated)
        {
            // not found ? add new reference
            if (destinationIndex == -1)
            {
                // Instantiate item copy
                var itemCopy = accessor.Instantiate(data.Item);
                UpdateBuffersAfterStructuraleChange(ref data);

                accessor.SetComponent(itemCopy, new FirstInstigator()
                {
                    Value = data.Destination.Value
                });

                destinationBuffer.Add(new InventoryItemReference()
                {
                    ItemEntity = itemCopy, Stacks = 0
                });
                destinationIndex = destinationBuffer.Length - 1;
            }

            // increase stacks
            var destinationEntry = destinationBuffer[destinationIndex];
            destinationEntry.Stacks            += data.Stacks;
            destinationBuffer[destinationIndex] = destinationEntry;
        }

        return(new ItemTransactionResult(data.Stacks));

        void UpdateBuffersAfterStructuraleChange(ref ItemTransationData data)
        {
            GetBuffer(accessor, data.Source, out data.SourceBuffer);
            GetBuffer(accessor, data.Destination, out data.DestinationBuffer);
            sourceBuffer      = data.SourceBuffer.GetValueOrDefault();
            destinationBuffer = data.DestinationBuffer.GetValueOrDefault();
        }
    }