 
     // MARK: - Cell Top Label Calculations
 
-    private func cellTopLabelWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
+    /// The total value of the horizontal insets for the cell top label.
+    private var topLabelHorizontalInsets: CGFloat {
+        return cellTopLabelInsets.left + cellTopLabelInsets.right
+    }
+
+    /// The total value of the vertical insets for the cell top label.
+    private var topLabelVerticalInsets: CGFloat {
+        return cellTopLabelInsets.top + cellTopLabelInsets.bottom
+    }
+
+    /// The max width available for the cell top label.
+    private func topLabelMaxWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
         if topLabelExtendsPastAvatar {
-            return itemWidth
+            return itemWidth - topLabelHorizontalInsets
         } else {
-            let avatarWidth = avatarSize(for: message, at: indexPath).width
-            return itemWidth - avatarWidth - avatarMessagePadding - messageToViewEdgePadding
-        }
-    }
-
-    private func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
-
-        guard let messagesCollectionView = messagesCollectionView else { return 0 }
-        guard let dataSource = messagesCollectionView.messagesDataSource else { return 0 }
-        guard let topLabelText = dataSource.cellTopLabelAttributedText(for: message, at: indexPath) else { return 0 }
-
-        let availableWidth = cellTopLabelWidth(for: message, at: indexPath) - cellTopLabelInsets.left - cellTopLabelInsets.right
-
-        let estimatedHeight = topLabelText.height(considering: availableWidth)
-
-        return estimatedHeight.rounded(.up)
-        
-    }
-
+            let reservedWidth = nonMessageContentWidth(for: message, at: indexPath) + topLabelHorizontalInsets
+            return itemWidth - reservedWidth
+        }
+    }
+
+    /// The size of the cell top label.
     fileprivate func cellTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
 
-        let topLabelWidth = cellTopLabelWidth(for: message, at: indexPath)
-        let topLabelHeight = cellTopLabelHeight(for: message, at: indexPath)
-
-        return CGSize(width: topLabelWidth, height: topLabelHeight)
-        
-    }
-
-    fileprivate func cellTopLabelOrigin(for attributes: MessagesCollectionViewLayoutAttributes) -> CGPoint {
-
-        var origin = CGPoint.zero
-
-        if topLabelExtendsPastAvatar == false {
-            let avatarWidth = attributes.avatarFrame.width
-            origin.x = avatarWidth + avatarMessagePadding
-        }
-
-        return origin
-
-    }
-
-    // MARK: - Cell Bottom Label Calculations
-
-    private func cellBottomLabelWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
-        if bottomLabelExtendsPastAvatar {
-            return itemWidth
-        } else {
-            let avatarWidth = avatarSize(for: message, at: indexPath).width
-            return itemWidth - avatarWidth - avatarMessagePadding - messageToViewEdgePadding
-        }
-    }
-
-    private func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
-
-        guard let messagesCollectionView = messagesCollectionView else { return 0 }
-        guard let dataSource = messagesCollectionView.messagesDataSource else { return 0 }
-        guard let bottomLabelText = dataSource.cellBottomLabelAttributedText(for: message, at: indexPath) else { return 0 }
-        let bottomLabelHorizontalInsets = cellBottomLabelInsets.left + cellBottomLabelInsets.right
-        let availableWidth = cellBottomLabelWidth(for: message, at: indexPath) - bottomLabelHorizontalInsets
-
-        let estimatedHeight = bottomLabelText.height(considering: availableWidth)
-
-        return estimatedHeight.rounded(.up)
-    }
-
-    fileprivate func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
-
-        let bottomLabelWidth = cellBottomLabelWidth(for: message, at: indexPath)
-        let bottomLabelHeight = cellBottomLabelHeight(for: message, at: indexPath)
-
-        return CGSize(width: bottomLabelWidth, height: bottomLabelHeight)
-        
-    }
-
-    fileprivate func cellBottomLabelOrigin(for attributes: MessagesCollectionViewLayoutAttributes) -> CGPoint {
-
-        var origin = CGPoint.zero
-
-        let contentHeight = attributes.frame.height
-        let cellBottomLabelHeight = attributes.cellBottomLabelFrame.height
-
-        origin.y = contentHeight - cellBottomLabelHeight
-
-        if bottomLabelExtendsPastAvatar == false {
-            let avatarWidth = attributes.avatarFrame.width
-            origin.x = avatarWidth + avatarMessagePadding
-        }
-
-        return origin
-
-    }
-
-    // MARK: - Cell Size Calculations
-
-    private func minimumCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
-
-        let avatarHeight = avatarSize(for: message, at: indexPath).height
-        let bottomLabelHeight = cellBottomLabelHeight(for: message, at: indexPath)
-        let topLabelHeight = cellTopLabelHeight(for: message, at: indexPath)
-
-        let minimumHeight = topLabelHeight + avatarHeight + bottomLabelHeight
-
-        return minimumHeight
-
-    }
-
-    private func estimatedCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
-
-        let containerHeight = messageContainerHeight(for: message, at: indexPath)
-        let topLabelHeight = cellTopLabelHeight(for: message, at: indexPath)
-        let bottomLabelheight = cellBottomLabelHeight(for: message, at: indexPath)
-
-        return topLabelHeight + containerHeight + bottomLabelheight
-
-    }
-
-    open func sizeForItem(at indexPath: IndexPath) -> CGSize {
-        
         guard let messagesCollectionView = messagesCollectionView else { return .zero }
         guard let dataSource = messagesCollectionView.messagesDataSource else { return .zero }
+        guard let topLabelText = dataSource.cellTopLabelAttributedText(for: message, at: indexPath) else { return .zero }
+
+        let maxWidth = topLabelMaxWidth(for: message, at: indexPath)
+        var size = labelSize(for: topLabelText, considering: maxWidth)
+
+        size.width += topLabelHorizontalInsets
+        size.height += topLabelVerticalInsets
+
+        return size
+
+    }
+
+    /// The origin for the cell top label.
+    fileprivate func cellTopLabelOrigin(for message: MessageType, and attributes: MessagesCollectionViewLayoutAttributes) -> CGPoint {
+
+        guard let messagesCollectionView = messagesCollectionView else { return .zero }
+        guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
+        let position = layoutDelegate.cellTopLabelPosition(for: message, at: attributes.indexPath, in: messagesCollectionView)
+
+        var origin: CGPoint = .zero
+
+        switch position {
+        case .cellTrailing:
+            origin.x = attributes.frame.width - attributes.cellTopLabelFrame.width
+        case .cellLeading:
+            origin.x = 0
+        case .cellCenter:
+            origin.x = itemWidth / 2 - (attributes.cellTopLabelFrame.width / 2)
+        case .messageTrailing:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding + attributes.messageContainerFrame.width - attributes.cellTopLabelFrame.width
+            case .outgoing:
+                origin.x = attributes.frame.width - attributes.avatarFrame.width - avatarMessagePadding - attributes.cellTopLabelFrame.width
+            }
+        case .messageLeading:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding
+            case .outgoing:
+                origin.x = attributes.frame.width - attributes.avatarFrame.width - avatarMessagePadding - attributes.messageContainerFrame.width
+            }
+        case .messageCenter:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding + (attributes.messageContainerFrame.width / 2) - (attributes.cellTopLabelFrame.width / 2)
+            case .outgoing:
+                origin.x = attributes.frame.width - attributes.avatarFrame.width - avatarMessagePadding - (attributes.messageContainerFrame.width / 2) - (attributes.cellTopLabelFrame.width / 2)
+            }
+        }
+        
+        return origin
+
+    }
+
+    // MARK: - Cell Bottom Label Calculations
+
+    /// The total value of the horizontal insets for the cell bottom label
+    private var bottomLabelHorizontalInsets: CGFloat {
+        return cellBottomLabelInsets.left + cellBottomLabelInsets.right
+    }
+
+    /// The total value of the vertical insets for the cell bottom label.
+    private var bottomLabelVerticalInsets: CGFloat {
+        return cellBottomLabelInsets.top + cellBottomLabelInsets.bottom
+    }
+
+    /// The max with available for the cell bottom label.
+    private func bottomLabelMaxWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
+        if bottomLabelExtendsPastAvatar {
+            return itemWidth - bottomLabelHorizontalInsets
+        } else {
+            let reservedWidth = nonMessageContentWidth(for: message, at: indexPath) + bottomLabelHorizontalInsets
+            return itemWidth - reservedWidth
+        }
+    }
+
+    /// The size for the cell bottom label.
+    fileprivate func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
+
+        guard let messagesCollectionView = messagesCollectionView else { return .zero }
+        guard let dataSource = messagesCollectionView.messagesDataSource else { return .zero }
+        guard let bottomLabelText = dataSource.cellBottomLabelAttributedText(for: message, at: indexPath) else { return .zero }
+
+        let maxWidth = bottomLabelMaxWidth(for: message, at: indexPath)
+        var size = labelSize(for: bottomLabelText, considering: maxWidth)
+
+        size.width += bottomLabelHorizontalInsets
+        size.height += bottomLabelVerticalInsets
+
+        return size
+        
+    }
+
+    /// The origin for the cell bottom label.
+    fileprivate func cellBottomLabelOrigin(for message: MessageType, and attributes: MessagesCollectionViewLayoutAttributes) -> CGPoint {
+
+        guard let messagesCollectionView = messagesCollectionView else { return .zero }
+        guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
+        let position = layoutDelegate.cellBottomLabelPosition(for: message, at: attributes.indexPath, in: messagesCollectionView)
+
+        var origin: CGPoint = .zero
+
+        origin.y = attributes.frame.height - attributes.cellBottomLabelFrame.height
+
+        switch position {
+        case .cellTrailing:
+            origin.x = attributes.frame.width - attributes.cellBottomLabelFrame.width
+        case .cellLeading:
+            origin.x = 0
+        case .cellCenter:
+            origin.x = itemWidth / 2 - (attributes.cellBottomLabelFrame.width / 2)
+        case .messageTrailing:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding + attributes.messageContainerFrame.width - attributes.cellBottomLabelFrame.width
+            case .outgoing:
+                origin.x = attributes.frame.width - attributes.avatarFrame.width - avatarMessagePadding - attributes.cellBottomLabelFrame.width
+            }
+        case .messageLeading:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding
+            case .outgoing:
+                origin.x = attributes.frame.width - attributes.avatarFrame.width - avatarMessagePadding - attributes.messageContainerFrame.width
+            }
+        case .messageCenter:
+            switch attributes.direction {
+            case .incoming:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding + (attributes.messageContainerFrame.width / 2) - (attributes.cellBottomLabelFrame.width / 2)
+            case .outgoing:
+                origin.x = attributes.avatarFrame.width + avatarMessagePadding + (attributes.messageContainerFrame.width / 2) - (attributes.cellBottomLabelFrame.width / 2)
+            }
+        }
+
+        return origin
+
+    }
+
+    // MARK: - Cell Size Calculations
+
+    /// The minimum height for the cell.
+    private func minimumCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
+
+        guard let messagesCollectionView = messagesCollectionView else { return 0 }
+        guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return 0 }
+
+        let avatarPosition = layoutDelegate.avatarPosition(for: message, at: indexPath, in: messagesCollectionView)
+
+        let topLabelHeight = cellTopLabelSize(for: message, at: indexPath).height
+        let avatarHeight = avatarSize(for: message, at: indexPath).height
+        let bottomLabelHeight = cellBottomLabelSize(for: message, at: indexPath).height
+
+        switch avatarPosition {
+        case .cellTop:
+            return max(avatarHeight, topLabelHeight) + bottomLabelHeight
+        case .cellBottom:
+            return max(avatarHeight, bottomLabelHeight) + topLabelHeight
+        case .messageTop, .messageCenter, .messageBottom:
+            return topLabelHeight + avatarHeight + bottomLabelHeight
+        }
+
+    }
+
+    /// The calculated height of the cell not considering the minimum height.
+    private func estimatedCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
+
+        let messageContainerHeight = messageContainerSize(for: message, at: indexPath).height
+        let cellTopLabelHeight = cellTopLabelSize(for: message, at: indexPath).height
+        let cellBottomLabelHeight = cellBottomLabelSize(for: message, at: indexPath).height
+
+        return messageContainerHeight + cellBottomLabelHeight + cellTopLabelHeight
+
+    }
+
+    /// The size for the cell considering all cell contents.
+    open func sizeForItem(at indexPath: IndexPath) -> CGSize {
+        
+        guard let messagesCollectionView = messagesCollectionView else { return .zero }
+        guard let dataSource = messagesCollectionView.messagesDataSource else { return .zero }
         
         let message = dataSource.messageForItem(at: indexPath, in: messagesCollectionView)
         
         let minimumHeight = minimumCellHeight(for: message, at: indexPath)
         let estimatedHeight = estimatedCellHeight(for: message, at: indexPath)
-        let finalHeight = estimatedHeight < minimumHeight ? minimumHeight : estimatedHeight
-        
-        return CGSize(width: itemWidth, height: finalHeight)
+
+        let itemHeight = estimatedHeight > minimumHeight ? estimatedHeight : minimumHeight
+
+        return CGSize(width: itemWidth, height: itemHeight)
         
     }
     

            let avatarMidY = avatarHeight / 2
            origin.y = contentHeight - cellTopLabelHeight - messageMidY - avatarMidY
        }

        return origin

    }

    // MARK: - Message Container Calculations

    private func availableMessageContainerWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {

        let avatarWidth = avatarSize(for: message, at: indexPath).width
        let avatarWidthPlusPadding = avatarWidth > 0 ? avatarWidth + avatarMessagePadding : 0

        let horizontalMessageInsets = messageLabelInsets.left + messageLabelInsets.right
        let availableWidth = itemWidth - avatarWidthPlusPadding - horizontalMessageInsets - messageToViewEdgePadding

        return availableWidth

    }

    private func messageContainerWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat {

        let containerHeight = messageContainerHeight(for: message, at: indexPath)
        let availableWidth = availableMessageContainerWidth(for: message, at: indexPath)
        let horizontalMessageInsets = messageLabelInsets.left + messageLabelInsets.right
        var estimatedWidth: CGFloat = 0

        switch message.data {
        case .text(let text):
            estimatedWidth = text.width(considering: containerHeight, and: messageLabelFont).rounded(.up)
        case .attributedText(let text):
            estimatedWidth = text.width(considering: containerHeight).rounded(.up)
        }

        let widthToUse = estimatedWidth > availableWidth ? availableWidth : estimatedWidth

        let finalWidth = widthToUse + horizontalMessageInsets

        return finalWidth
        
    }

    private func messageContainerHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {

        let availableWidth = availableMessageContainerWidth(for: message, at: indexPath)
        let verticalMessageInsets = messageLabelInsets.top + messageLabelInsets.bottom
        var estimatedHeight: CGFloat = 0

        switch message.data {
        case .text(let text):
            estimatedHeight = text.height(considering: availableWidth, and: messageLabelFont)
        case .attributedText(let text):
            estimatedHeight = text.height(considering: availableWidth)
        }

        let finalHeight = estimatedHeight.rounded(.up) + verticalMessageInsets

        return finalHeight
        
    }

    fileprivate func messageContainerSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {

        let messageWidth = messageContainerWidth(for: message, at: indexPath)
        let messageHeight = messageContainerHeight(for: message, at: indexPath)

        return CGSize(width: messageWidth, height: messageHeight)

    }

    fileprivate func messageContainerOrigin(for attributes: MessagesCollectionViewLayoutAttributes) -> CGPoint {

        let contentWidth = attributes.frame.width


    // MARK: - Avatar Calculations

    /// The size of the avatar view.
    fileprivate func avatarSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {

        guard let messagesCollectionView = messagesCollectionView else { return .zero }
