//
//  TapUIChatViewController.m
//  TapTalk
//
//  Created by Dominic Vedericho on 10/08/18.
//  Copyright © 2018 Moselo. All rights reserved.
//

#import "TapUIChatViewController.h"

#import <AFNetworking/AFNetworking.h>
#import <Photos/Photos.h>
#import <MobileCoreServices/MobileCoreServices.h>

#import "TAPCustomAccessoryView.h"
#import "TAPGradientView.h"
#import "TAPCustomButtonView.h"

#import "TAPConnectionStatusViewController.h"
#import "TAPKeyboardViewController.h"
#import "TAPProfileViewController.h"
#import "TAPImagePreviewViewController.h"
#import "TAPPhotoAlbumListViewController.h"
#import "TAPPickLocationViewController.h"
#import "TAPForwardListViewController.h"
#import "TAPWebViewViewController.h"
#import "TAPMediaDetailViewController.h"

#import "TAPMyChatBubbleTableViewCell.h"
#import "TAPYourChatBubbleTableViewCell.h"
#import "TAPMyFileBubbleTableViewCell.h"
#import "TAPYourFileBubbleTableViewCell.h"
#import "TAPMyImageBubbleTableViewCell.h"
#import "TAPYourImageBubbleTableViewCell.h"
#import "TAPMyLocationBubbleTableViewCell.h"
#import "TAPYourLocationBubbleTableViewCell.h"
#import "TAPMyVideoBubbleTableViewCell.h"
#import "TAPYourVideoBubbleTableViewCell.h"
#import "TAPMyChatDeletedBubbleTableViewCell.h"
#import "TAPYourChatDeletedBubbleTableViewCell.h"

#import "TAPProductListBubbleTableViewCell.h"
#import "TAPUnreadMessagesBubbleTableViewCell.h"
#import "TAPLoadingTableViewCell.h"
#import "TAPSystemMessageTableViewCell.h"

#import "TAPQuoteModel.h"

@import QuickLook;

static const NSInteger kShowChatAnchorOffset = 70.0f;
static const NSInteger kChatAnchorDefaultBottomConstraint = 63.0f;
static const NSInteger kInputMessageAccessoryViewHeight = 52.0f;
static const NSInteger kInputMessageAccessoryExtensionViewDefaultHeight = 68.0f;

typedef NS_ENUM(NSInteger, KeyboardState) {
    keyboardStateDefault = 0,
    keyboardStateOptions = 1,
};

typedef NS_ENUM(NSInteger, InputAccessoryExtensionType) {
    inputAccessoryExtensionTypeQuote = 0,
    inputAccessoryExtensionTypeReplyMessage = 1,
};

typedef NS_ENUM(NSInteger, LoadMoreMessageViewType) {
    LoadMoreMessageViewTypeOlderMessage = 0,
    LoadMoreMessageViewTypeNewMessage = 1,
};

typedef NS_ENUM(NSInteger, TopFloatingIndicatorViewType) {
    TopFloatingIndicatorViewTypeUnreadMessage = 0,
    TopFloatingIndicatorViewTypeLoading = 1
};

@interface TapUIChatViewController () <UIGestureRecognizerDelegate, UINavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource, UIDocumentPickerDelegate, UIImagePickerControllerDelegate, QLPreviewControllerDelegate, QLPreviewControllerDataSource, UIAdaptivePresentationControllerDelegate, TAPGrowingTextViewDelegate, TAPChatManagerDelegate, TAPConnectionStatusViewControllerDelegate, TAPImagePreviewViewControllerDelegate, TAPMediaDetailViewControllerDelegate, TAPPhotoAlbumListViewControllerDelegate, TAPPickLocationViewControllerDelegate, TAPMyChatBubbleTableViewCellDelegate, TAPYourChatBubbleTableViewCellDelegate, TAPMyImageBubbleTableViewCellDelegate, TAPYourImageBubbleTableViewCellDelegate, TAPProductListBubbleTableViewCellDelegate, TAPMyLocationBubbleTableViewCellDelegate, TAPYourLocationBubbleTableViewCellDelegate, TAPMyFileBubbleTableViewCellDelegate, TAPYourFileBubbleTableViewCellDelegate, TAPMyVideoBubbleTableViewCellDelegate, TAPYourVideoBubbleTableViewCellDelegate, TAPMyChatDeletedBubbleTableViewCellDelegate, TAPYourChatDeletedBubbleTableViewCellDelegate, TAPProfileViewControllerDelegate>

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageTextViewHeightConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewHeightConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewLeftConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *keyboardOptionViewRightConstraint;
@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *tableViewTopConstraint;
@property (strong, nonatomic) IBOutlet UIView *textViewBorderView;
@property (strong, nonatomic) IBOutlet TAPGrowingTextView *messageTextView;
@property (strong, nonatomic) IBOutlet UITextField *secondaryTextField;
@property (strong, nonatomic) IBOutlet UIView *emptyView;
@property (strong, nonatomic) IBOutlet UILabel *emptyTitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *emptyDescriptionLabel;
@property (strong, nonatomic) IBOutlet UIView *senderInitialNameView;
@property (strong, nonatomic) IBOutlet UILabel *senderInitialNameLabel;
@property (strong, nonatomic) IBOutlet TAPImageView *senderImageView;
@property (strong, nonatomic) IBOutlet UIView *recipientInitialNameView;
@property (strong, nonatomic) IBOutlet UILabel *recipientInitialNameLabel;
@property (strong, nonatomic) IBOutlet TAPImageView *recipientImageView;
@property (strong, nonatomic) IBOutlet TAPCustomAccessoryView *inputMessageAccessoryView;
@property (strong, nonatomic) IBOutlet UIImageView *inputMessageAccessoryCloseImageView;
@property (strong, nonatomic) IBOutlet UIImageView *inputMessageAccessoryDocumentsImageView;
@property (strong, nonatomic) IBOutlet UIView *sendButtonView;
@property (strong, nonatomic) IBOutlet UIImageView *sendButtonImageView;
@property (strong, nonatomic) IBOutlet UIButton *sendButton;
@property (strong, nonatomic) IBOutlet UIView *keyboardOptionButtonView;
@property (strong, nonatomic) IBOutlet UIImageView *keyboardOptionButtonImageView;
@property (strong, nonatomic) IBOutlet UIButton *keyboardOptionButton;
@property (strong, nonatomic) IBOutlet UIView *quoteStandingSeparatorView;

@property (strong, nonatomic) IBOutlet UIView *dummyNavigationBarView;
@property (strong, nonatomic) IBOutlet UILabel *dummyNavigationBarTitleLabel;

@property (strong, nonatomic) IBOutlet UIView *topFloatingIndicatorView;
@property (strong, nonatomic) IBOutlet UILabel *topFloatingIndicatorLabel;
@property (strong, nonatomic) IBOutlet UIImageView *topFloatingIndicatorImageView;
@property (strong, nonatomic) IBOutlet UIButton *topFloatingIndicatorButton;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topFloatingIndicatorWidthConstraint;

@property (strong, nonatomic) IBOutlet UIImageView *chatAnchorImageView;

@property (strong, nonatomic) IBOutlet UIButton *attachmentButton;

@property (nonatomic) TopFloatingIndicatorViewType topFloatingIndicatorViewType;

@property (strong, nonatomic) UIView *titleView;
@property (strong, nonatomic) UILabel *nameLabel;
@property (strong, nonatomic) UIView *userDescriptionView;
@property (strong, nonatomic) UIView *userStatusView;
@property (strong, nonatomic) UILabel *userStatusLabel;
@property (strong, nonatomic) NSTimer *lastSeenTimer;
@property (strong, nonatomic) UIView *userTypingView;
@property (strong, nonatomic) UILabel *typingLabel;

@property (strong, nonatomic) UIView *rightBarInitialNameView;
@property (strong, nonatomic) UILabel *rightBarInitialNameLabel;
@property (strong, nonatomic) TAPImageView *rightBarImageView;

@property (strong, nonatomic) TAPConnectionStatusViewController *connectionStatusViewController;
@property (strong, nonatomic) TAPKeyboardViewController *keyboardViewController;

@property (strong, atomic) NSMutableArray *messageArray; //RN Note - Use atomic for thread safety
@property (strong, atomic) NSMutableDictionary *messageDictionary;

@property (strong, nonatomic) NSMutableDictionary *cellHeightsDictionary;
@property (strong, nonatomic) TAPMessageModel *selectedMessage;
@property (strong, nonatomic) TAPOnlineStatusModel *onlineStatus;

@property (strong, nonatomic) NSNumber *minCreatedMessage;
@property (strong, nonatomic) NSNumber *loadedMaxCreated;

@property (strong, nonatomic) NSString *tappedMessageLocalID;

@property (strong, nonatomic) NSURL *currentSelectedFileURL;

@property (nonatomic) CGFloat messageTextViewHeight;
@property (nonatomic) CGFloat safeAreaBottomPadding;
@property (nonatomic) CGFloat keyboardHeight;
@property (nonatomic) CGFloat lastKeyboardHeight;
@property (nonatomic) CGFloat initialKeyboardHeight;
@property (nonatomic) CGFloat currentInputAccessoryExtensionHeight;

@property (nonatomic) long apiBeforeLastCreated;
@property (nonatomic) BOOL isLastPage;
@property (nonatomic) KeyboardState keyboardState;
@property (nonatomic) BOOL isKeyboardWasShowed;
@property (nonatomic) BOOL isKeyboardShowed;
@property (nonatomic) BOOL isScrollViewDragged;
@property (nonatomic) BOOL isCustomKeyboardAvailable;
@property (nonatomic) BOOL isViewWillAppeared;
@property (nonatomic) BOOL isViewDidAppeared;
@property (nonatomic) BOOL isKeyboardOptionTapped;
@property (nonatomic) BOOL isKeyboardShowedForFirstTime;
@property (nonatomic) BOOL isInputAccessoryExtensionShowedFirstTimeOpen;
@property (nonatomic) BOOL isTopFloatingIndicatorLoading;
@property (nonatomic) BOOL isUnreadButtonShown;
@property (nonatomic) BOOL isSwipeGestureEnded;
@property (nonatomic) BOOL isShowingTopFloatingIdentifier;

@property (nonatomic) CGFloat connectionStatusHeight;

@property (strong, nonatomic) IBOutlet UIView *chatAnchorBackgroundView;
@property (strong, nonatomic) IBOutlet UIButton *chatAnchorButton;
@property (strong, nonatomic) IBOutlet TAPGradientView *chatAnchorBadgeView;
@property (strong, nonatomic) IBOutlet UILabel *chatAnchorBadgeLabel;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorButtonBottomConstrait;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorBackgroundViewBottomConstrait;

//Input Accessory Extension
@property (strong, nonatomic) IBOutlet UIView *quoteView;
@property (strong, nonatomic) IBOutlet UILabel *quoteTitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *quoteSubtitleLabel;
@property (strong, nonatomic) IBOutlet TAPImageView *quoteImageView;
@property (strong, nonatomic) IBOutlet UIView *replyMessageView;
@property (strong, nonatomic) IBOutlet UIView *replyMessageInnerContainerView;
@property (strong, nonatomic) IBOutlet UIView *quoteFileView;
@property (strong, nonatomic) IBOutlet UILabel *replyMessageNameLabel;
@property (strong, nonatomic) IBOutlet UILabel *replyMessageMessageLabel;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *inputAccessoryExtensionHeightConstraint;
@property (nonatomic) InputAccessoryExtensionType inputAccessoryExtensionType;

//Load More Message Loading View
@property (strong, nonatomic) IBOutlet UIView *loadMoreMessageLoadingView;
@property (strong, nonatomic) IBOutlet UILabel *loadMoreMessageLoadingLabel;
@property (strong, nonatomic) IBOutlet UIImageView *loadMoreMessageLoadingViewImageView;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *loadMoreMessageLoadingHeightConstraint;
@property (nonatomic) CGFloat loadMoreMessageViewHeight;

//Deleted Room View
@property (strong, nonatomic) IBOutlet UIView *deletedRoomView;
@property (strong, nonatomic) IBOutlet UILabel *deletedRoomTitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *deletedRoomContentLabel;
@property (strong, nonatomic) IBOutlet UIView *deleteRoomButtonContainerView;
@property (strong, nonatomic) IBOutlet UIImageView *deletedRoomIconImageView;
@property (strong, nonatomic) IBOutlet UIView *deleteRoomButtonView;
@property (strong, nonatomic) IBOutlet UILabel *deleteRoomButtonLabel;
@property (strong, nonatomic) IBOutlet UIImageView *deleteRoomButtonIconImageView;
@property (strong, nonatomic) IBOutlet UIButton *deleteRoomButton;
@property (strong, nonatomic) IBOutlet UIImageView *deleteRoomButtonLoadingImageView;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint;

//Kicked or Removed Group Room View
@property (strong, nonatomic) IBOutlet UIView *kickedGroupRoomBackgroundView;
@property (strong, nonatomic) IBOutlet UIView *kickedGroupRoomInfoView;
@property (strong, nonatomic) IBOutlet UILabel *kickedGroupRoomInfoLabel;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *deletedRoomViewHeightConstraint;

//Add to Contacts View
@property (strong, nonatomic) IBOutlet UIView *addToContactContainerView;
@property (strong, nonatomic) IBOutlet UIView *addContactView;
@property (strong, nonatomic) IBOutlet UILabel *addContactLabel;
@property (strong, nonatomic) IBOutlet UIButton *addContactButton;
@property (strong, nonatomic) IBOutlet UIView *blockContactView;
@property (strong, nonatomic) IBOutlet UILabel *blockContactLabel;
@property (strong, nonatomic) IBOutlet UIButton *blockContactButton;
@property (strong, nonatomic) IBOutlet UIView *closeButtonView;
@property (strong, nonatomic) IBOutlet UIImageView *closeButtonImageView;
@property (strong, nonatomic) IBOutlet UIButton *closeButton;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *blockUserViewWidthConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewWidthConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewLeftConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewHeightConstraint;
- (IBAction)blockUserButtonDidTapped:(id)sender;
- (IBAction)addContactButtonDidTapped:(id)sender;
- (IBAction)closeAddContactButtonDidTapped:(id)sender;

@property (strong, nonatomic) NSMutableArray *anchorUnreadMessageArray;
@property (strong, nonatomic) NSMutableArray *scrolledPendingMessageArray;

@property (nonatomic) BOOL isOnScrollPendingChecking;
@property (nonatomic) BOOL isNeedRefreshOnNetworkDown;
@property (nonatomic) BOOL isShowAccessoryView;
@property (nonatomic) BOOL isFirstLoadData;
@property (nonatomic) BOOL isLoadingOldMessageFromAPI;

@property (nonatomic) NSInteger lastLoadingCellRowPosition;

@property (strong, nonatomic) TAPUserModel *otherUser;
@property (nonatomic) BOOL isOtherUserIsContact;

@property (strong, nonatomic) NSString *unreadLocalID;
@property (nonatomic) NSInteger numberOfUnreadMessages;
@property (nonatomic) BOOL isShowingUnreadMessageIdentifier;

@property (weak, nonatomic) id openedBubbleCell;

//Custom Method
- (void)setupNavigationViewData;
- (void)setupInputAccessoryView;
- (void)setupDeletedRoomView;
- (void)showDeletedRoomView:(BOOL)show isGroup:(BOOL)isGroup;
- (void)setDeleteRoomButtonAsLoading:(BOOL)loading animated:(BOOL)animated;
- (void)setupKickedGroupView;
- (void)checkAndSetupAddToContactsView;
- (void)checkIsContainQuoteMessage;
- (void)setSendButtonActive:(BOOL)isActive;
- (IBAction)sendButtonDidTapped:(id)sender;
- (IBAction)handleTapOnTableView:(UITapGestureRecognizer *)gestureRecognizer;
- (IBAction)chatAnchorButtonDidTapped:(id)sender;
- (IBAction)inputAccessoryExtensionCloseButtonDidTapped:(id)sender;
- (IBAction)topFloatingIndicatorButtonDidTapped:(id)sender;
- (IBAction)deleteGroupButtonDidTapped:(id)sender;
- (void)backButtonDidTapped;
- (void)addIncomingMessageToArrayAndDictionaryWithMessage:(TAPMessageModel *)message atIndex:(NSInteger)index;
- (void)removeMessageFromArrayAndDictionaryWithLocalID:(NSString *)localID;
- (void)handleMessageFromSocket:(TAPMessageModel *)message isUpdatedMessage:(BOOL)isUpdated;
- (void)destroySequence;
- (void)firstLoadData;
- (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated;
- (void)retrieveExistingMessages;
- (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray checkFirstUnreadMessage:(BOOL)checkFirstUnreadMessage toTop:(BOOL)toTop updateUserDetail:(BOOL)updateUserDetail withCompletionHandler:(void(^)())completionHandler;
- (void)sortAndFilterMessageArray;
- (void)updateMessageModelValueWithMessage:(TAPMessageModel *)message;
- (void)callAPIAfterAndUpdateUIAndScrollToTop:(BOOL)scrollToTop;
- (void)saveMessageDraft;
- (void)applicationWillEnterForegroundNotification:(NSNotification *)notification;
- (void)checkAnchorUnreadLabel;
- (void)addMessageToAnchorUnreadArray:(TAPMessageModel *)message;
- (void)removeMessageFromAnchorUnreadArray:(TAPMessageModel *)message;
- (void)timerRefreshLastSeen;
- (void)updateLastSeenWithTimestamp:(NSTimeInterval)timestamp;
- (void)processMessageAsRead:(TAPMessageModel *)message forceMarkAsRead:(BOOL)force;
- (void)processVisibleMessageAsRead;
- (void)processAllPreviousMessageAsRead;
- (void)setAsTyping:(BOOL)typing;
- (void)setAsTypingNoAfterDelay;
- (void)showInputAccessoryExtensionView:(BOOL)show;
- (void)setInputAccessoryExtensionType:(InputAccessoryExtensionType)inputAccessoryExtensionType;
- (void)showInputAccessoryView;

- (void)showLoadMoreMessageLoadingView:(BOOL)show
                              withType:(LoadMoreMessageViewType)type;
- (void)showTopFloatingIdentifierView:(BOOL)show
                             withType:(TopFloatingIndicatorViewType)type
               numberOfUnreadMessages:(NSInteger)numberOfUnreadMessages
                             animated:(BOOL)animated;
- (void)showLoadMessageCellLoading:(BOOL)show;
- (void)setReplyMessageWithMessage:(TAPMessageModel *)message;
- (void)setQuoteWithQuote:(TAPQuoteModel *)quote userID:(NSString *)userID;
- (void)showImagePreviewControllerWithSelectedImage:(UIImage *)image;
- (void)fetchImageDataWithMessage:(TAPMessageModel *)message;
- (void)fetchFileDataWithMessage:(TAPMessageModel *)message;
- (void)fetchVideoDataWithMessage:(TAPMessageModel *)message;
- (void)handleLongPressedWithURL:(NSURL *)url originalString:(NSString *)originalString;
- (void)handleLongPressedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString;
- (void)handleTappedWithURL:(NSURL *)url originalString:(NSString *)originalString;
- (void)handleTappedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString;
- (void)handleLongPressedWithMessage:(TAPMessageModel *)message;
- (void)openFiles;
- (void)openCamera;
- (void)openGallery;
- (void)pickLocation;
- (void)openLocationInGoogleMaps:(NSDictionary *)dataDictionary;
- (void)openLocationInAppleMaps:(NSDictionary *)dataDictionary;
- (void)checkAndRefreshOnlineStatus;
- (void)scrollToFirstUnreadMessage;
- (void)scrollToMessageAndLoadDataWithLocalID:(NSString *)localID;
- (BOOL)checkIsRowVisibleWithRowIndex:(NSInteger)rowIndex;
- (void)checkAndShowUnreadButton;
- (void)showDeleteMessageActionWithMessageArray:(NSString *)deletedMessageIDArray;

- (void)fileUploadManagerProgressNotification:(NSNotification *)notification;
- (void)fileUploadManagerStartNotification:(NSNotification *)notification;
- (void)fileUploadManagerFinishNotification:(NSNotification *)notification;
- (void)fileUploadManagerFailureNotification:(NSNotification *)notification;
- (void)userProfileDidChangeNotification:(NSNotification *)notification;

- (void)fileDownloadManagerProgressNotification:(NSNotification *)notification;
- (void)fileDownloadManagerStartNotification:(NSNotification *)notification;
- (void)fileDownloadManagerFinishNotification:(NSNotification *)notification;
- (void)fileDownloadManagerFailureNotification:(NSNotification *)notification;

- (void)applicationDidBecomeActiveNotification:(NSNotification *)notification;

- (void)refreshRoomStatusUIInfo;
- (void)refreshTypingLabelState;

- (void)checkAndShowRoomViewState;

@end

@implementation TapUIChatViewController

#pragma mark - Lifecycle
- (instancetype)initWithOtherUser:(TAPUserModel *)otherUser {
    if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) {
        TAPRoomModel *roomData = [TAPRoomModel createPersonalRoomIDWithOtherUser:otherUser];
        self.currentRoom = roomData;
    }
    return self;
}

- (instancetype)initWithRoom:(TAPRoomModel *)room {
    if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) {
        self.currentRoom = room;
    }
    return self;
}

- (instancetype)initWithRoom:(TAPRoomModel *)room scrollToMessageWithLocalID:(NSString *)messageLocalID {
    if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) {
        self.currentRoom = room;
        self.scrollToMessageLocalIDString = messageLocalID;
    }
    return self;
}

- (void)loadView {
    [super loadView];
    
    id quotedMessage = [[TAPChatManager sharedManager] getQuotedMessageObjectWithRoomID:self.currentRoom.roomID];
    CGFloat extensionHeight = 0.0f;
    if(quotedMessage) {
        extensionHeight = kInputMessageAccessoryExtensionViewDefaultHeight;
        _isInputAccessoryExtensionShowedFirstTimeOpen = YES;
    }
    
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.0];
    [UIView setAnimationDelay:0.0];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    [UIView performWithoutAnimation:^{
        self.tableView.frame = CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds) - [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO] - kInputMessageAccessoryViewHeight - extensionHeight - [TAPUtil safeAreaBottomPadding]);
    }];
    [UIView commitAnimations];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    //Open room and save to active room
    [[TAPChatManager sharedManager] openRoom:self.currentRoom];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityStatusChange:) name:TAP_NOTIFICATION_REACHABILITY_STATUS_CHANGED object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerProgressNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_PROGRESS object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerStartNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_START object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerFinishNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_FINISH object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerFailureNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_FAILURE object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerProgressNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_PROGRESS object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerStartNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_START object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerFinishNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_FINISH object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerFailureNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_FAILURE object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userProfileDidChangeNotification:) name:TAP_NOTIFICATION_USER_PROFILE_CHANGES object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActiveNotification:) name:TAP_NOTIFICATION_APPLICATION_DID_BECOME_ACTIVE object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:TAP_NOTIFICATION_APPLICATION_WILL_ENTER_FOREGROUND object:nil];
    
    [[TAPChatManager sharedManager] addDelegate:self];
    self.navigationController.delegate = self;
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
    
    //Initialization Data
    _messageArray = [[NSMutableArray alloc] init];
    _messageDictionary = [[NSMutableDictionary alloc] init];
    _cellHeightsDictionary = [[NSMutableDictionary alloc] init];
    _anchorUnreadMessageArray = [[NSMutableArray alloc] init];
    _scrolledPendingMessageArray = [[NSMutableArray alloc] init];
    _otherUser = nil;
    _isKeyboardWasShowed = NO;
    _isKeyboardShowed = NO;
    _isShowAccessoryView = YES;
    _isShowingUnreadMessageIdentifier = NO;
    _tappedMessageLocalID = @"";
    _safeAreaBottomPadding = [TAPUtil safeAreaBottomPadding];
    _selectedMessage = nil;
    
    _keyboardState = keyboardStateDefault;
    _keyboardHeight = 0.0f;
    _initialKeyboardHeight = 0.0f;
    _lastKeyboardHeight = 0.0f;
    
    self.messageTextViewHeight = 32.0f;
    self.messageTextView.delegate = self;
    self.textViewBorderView.layer.cornerRadius = 18.0f;
    self.textViewBorderView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorChatComposerBackground];
    self.textViewBorderView.layer.borderColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorTextFieldBorderInactive].CGColor;
    self.textViewBorderView.layer.borderWidth = 1.0f;
    self.textViewBorderView.clipsToBounds = YES;
    self.messageTextView.minimumHeight = 32.0f;
    self.messageTextView.maximumHeight = 64.0f;
    
    if (IS_IPHONE_X_FAMILY) {
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding;
    }
    
    self.chatAnchorBadgeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.chatAnchorBadgeView.layer.borderColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground].CGColor;
    self.chatAnchorBadgeView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground];
    self.chatAnchorBadgeView.layer.borderWidth = 1.0f;
    self.chatAnchorBadgeView.layer.cornerRadius = CGRectGetHeight(self.chatAnchorBadgeView.frame) / 2.0f;
    self.chatAnchorBackgroundView.layer.cornerRadius = CGRectGetHeight(self.chatAnchorBackgroundView.frame) / 2.0f;
    
    //Rotate table view and commit animation
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.0];
    [UIView setAnimationDelay:0.0];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    
    [self.tableView setTransform:CGAffineTransformMakeRotation(-M_PI)];
    self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0f, 0.0f, 58.0f, CGRectGetWidth([UIScreen mainScreen].bounds) - 10.0f);
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    [UIView commitAnimations];
    
    self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 58.0f, 0.0f);
    
    self.view.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDefaultBackground];
    self.quoteStandingSeparatorView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorQuoteLayoutDecorationBackground];
    
    //Connection status view
    _connectionStatusViewController = [[TAPConnectionStatusViewController alloc] init];
    [self addChildViewController:self.connectionStatusViewController];
    [self.connectionStatusViewController didMoveToParentViewController:self];
    self.connectionStatusViewController.delegate = self;
    [self.view addSubview:self.connectionStatusViewController.view];
    _connectionStatusHeight = CGRectGetHeight(self.connectionStatusViewController.view.frame);
    
    //Custom Keyboard
    _keyboardViewController = [[TAPKeyboardViewController alloc] initWithNibName:@"TAPKeyboardViewController" bundle:[TAPUtil currentBundle]];
    TAPUserModel *currentUser = [TAPDataManager getActiveUser];
    NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID];
    if (self.currentRoom.type == RoomTypePersonal) {
        _otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID];
        if (self.otherUser == nil) {
            self.inputMessageAccessoryView.alpha = 0.0f;
        }
    }
    
    NSArray *keyboardArray = [NSArray array];
    id<TapUICustomKeyboardDelegate> customKeyboardDelegate = [TapUI sharedInstance].customKeyboardDelegate;
    if ([customKeyboardDelegate respondsToSelector:@selector(setCustomKeyboardItemsForRoom:sender:recipient:)]) {
        keyboardArray = [customKeyboardDelegate setCustomKeyboardItemsForRoom:self.currentRoom sender:currentUser recipient:self.otherUser];
    }
    
    if([keyboardArray count] > 0) {
        //There's custom keyboard for this type
        [self.keyboardViewController setCustomKeyboardArray:keyboardArray sender:currentUser recipient:self.otherUser room:self.currentRoom];
        _isCustomKeyboardAvailable = YES;
    }
    else {
        //There's no custom keyboard for this type
        _isCustomKeyboardAvailable = NO;
        self.keyboardOptionButtonView.alpha = 0.0f;
        self.keyboardOptionButton.alpha = 0.0f;
        self.keyboardOptionButton.userInteractionEnabled = NO;
        self.messageViewLeftConstraint.constant = -38.0f;
        self.keyboardOptionViewRightConstraint.constant = -26.0f;
    }
    //END Custom Keyboard
    
    _lastSeenTimer = [NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(timerRefreshLastSeen) userInfo:nil repeats:YES];
    
    [self setupNavigationViewData];

    [self setupInputAccessoryView];
    [self setupDeletedRoomView];
    [self setupKickedGroupView];
    
    //load data
    [self firstLoadData];
    
    [[TAPChatManager sharedManager] refreshShouldRefreshOnlineStatus];
    
    if (self.chatViewControllerType == TapUIChatViewControllerTypePeek) {
        //Hide accessory view when peek 3D touch
        self.inputMessageAccessoryView.alpha = 0.0f;
        self.dummyNavigationBarView.alpha = 1.0f;
        self.dummyNavigationBarTitleLabel.alpha = 1.0f;
        self.dummyNavigationBarTitleLabel.text = self.currentRoom.name;
    }
    
    //Set top floating indicator view label color
    UIFont *unreadMessageIdentifierButtonLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontUnreadMessageButtonLabel];
    UIColor *unreadMessageIdentifierButtonLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorUnreadMessageButtonLabel];
    self.topFloatingIndicatorLabel.font = unreadMessageIdentifierButtonLabelFont;
    self.topFloatingIndicatorLabel.textColor = unreadMessageIdentifierButtonLabelColor;
    self.topFloatingIndicatorView.layer.cornerRadius = 8.0f;
    self.topFloatingIndicatorView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.1f].CGColor;
    self.topFloatingIndicatorView.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
    self.topFloatingIndicatorView.layer.shadowOpacity = 1.0f;
    self.topFloatingIndicatorView.layer.shadowRadius = 4.0f;
    [self.topFloatingIndicatorButton addTarget:self action:@selector(topFloatingIndicatorButtonDidTapped:) forControlEvents:UIControlEventTouchUpInside];
    
    //check if there's scroll to message passed from open room method
    //if yes scroll to message after open chat room
    if (![TAPUtil isEmptyString:self.scrollToMessageLocalIDString]) {
        [self scrollToMessageAndLoadDataWithLocalID:self.scrollToMessageLocalIDString];
    }
    
    self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    //Open room and save to active room
    //Check when isFirstLoadData is false because we already called open room in viewDidLoad method to prevent double called
    if (!self.isFirstLoadData) {
        [[TAPChatManager sharedManager] openRoom:self.currentRoom];
    }
    
    [self.navigationController setNavigationBarHidden:NO animated:YES];
    
    self.connectionStatusViewController.isChatViewControllerAppear = self.isViewWillAppeared;
    self.connectionStatusViewController.view.frame = CGRectMake(CGRectGetMinX(self.connectionStatusViewController.view.frame), CGRectGetMinY(self.connectionStatusViewController.view.frame), CGRectGetWidth(self.connectionStatusViewController.view.frame), self.connectionStatusHeight);
    if (IS_IOS_11_OR_ABOVE) {
        [self.tableView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
    }
    
    if (self.currentRoom.isDeleted) {
        return;
    }
    
    _isViewWillAppeared = YES;
    _isSwipeGestureEnded = NO;
    if (!self.currentRoom.isDeleted) {
        _isShowAccessoryView = YES;
    }
    [self reloadInputViews];
    
    //Check chat room contains mesage draft or not
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    NSString *draftMessage = [[TAPChatManager sharedManager] getMessageFromDraftWithRoomID:roomID];
    draftMessage = [TAPUtil nullToEmptyString:draftMessage];
    
    [self.messageTextView setInitialText:draftMessage];
    if (![self.messageTextView.text isEqualToString:@""]) {
        [self setSendButtonActive:YES];
        
        if(self.isCustomKeyboardAvailable) {
            [UIView animateWithDuration:0.2f animations:^{
                self.keyboardOptionButtonView.alpha = 1.0f;
                self.keyboardOptionButton.alpha = 1.0f;
                self.keyboardOptionButton.userInteractionEnabled = YES;
                self.messageViewLeftConstraint.constant = 4.0f;
                self.keyboardOptionViewRightConstraint.constant = 16.0f;
                [self.view layoutIfNeeded];
            } completion:^(BOOL finished) {
                //Do something after animation completed.
            }];
        }
    }
    else {
        [self checkIsContainQuoteMessage];
    }
    
    [self checkAndRefreshOnlineStatus];
    [self setKeyboardStateDefault];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    _isViewDidAppeared = YES;
    
    [self.navigationController.interactivePopGestureRecognizer addTarget:self
                                                                  action:@selector(handleNavigationPopGesture:)];
    
    if (self.initialKeyboardHeight == 0.0f) {
        [UIView performWithoutAnimation:^{
            [self.messageTextView becameFirstResponder];
        }];
        
        [UIView performWithoutAnimation:^{
            [self.messageTextView resignFirstResponder];
        }];
        _isKeyboardShowed = NO;
    }
    
    [self processVisibleMessageAsRead];

    //check if last message is deleted room
    TAPMessageModel *lastMessage = [self.messageArray firstObject];
    if (lastMessage.room.type == RoomTypePersonal && lastMessage.room.isDeleted) {
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
        //Check if system message with action remove participant and target user is current user
        //show deleted chat room view
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO];
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/delete"]) {
        [self.view endEditing:YES];
        
        if (lastMessage.room.type == RoomTypePersonal) {
            [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
        }
        else if (lastMessage.room.type == RoomTypeGroup) {
            [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES];
        }
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/leave"] && [lastMessage.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
    }
    else {
        [self showInputAccessoryView];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    _isViewWillAppeared = NO;
    self.connectionStatusViewController.isChatViewControllerAppear = self.isViewWillAppeared;
    
    //Save existing message to draft
    [self saveMessageDraft];
    
    if([self.delegate respondsToSelector:@selector(chatViewControllerShouldUpdateUnreadBubbleForRoomID:)]) {
        [self.delegate chatViewControllerShouldUpdateUnreadBubbleForRoomID:self.currentRoom.roomID];
    }
    
    _keyboardHeight = self.inputAccessoryExtensionHeightConstraint.constant + self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight;
    
    [[TAPChatManager sharedManager] saveAllUnsentMessageInMainThread];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    _isViewDidAppeared = NO;
    
    [self.navigationController.interactivePopGestureRecognizer removeTarget:self action:@selector(handleNavigationPopGesture:)];
    
    //stop typing animation sequence
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    //Override present view controller method to resign keyboard before presenting view controller from Chat Room to avoid keyboard accessory missing after VC presented from Chat Room
    [self.secondaryTextField resignFirstResponder];
    [self.messageTextView resignFirstResponder];
    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_APPLICATION_WILL_ENTER_FOREGROUND object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_REACHABILITY_STATUS_CHANGED object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_PROGRESS object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_START object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_FINISH object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_FAILURE object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_PROGRESS object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_START object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_FINISH object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_FAILURE object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_USER_PROFILE_CHANGES object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_APPLICATION_DID_BECOME_ACTIVE object:nil];
}

#pragma mark - Data Source
#pragma mark UITableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (self.isLoadingOldMessageFromAPI && [self.messageArray count] > 0 && !self.isShowingTopFloatingIdentifier) {
        return [self.messageArray count] + 1;
    }
    return [self.messageArray count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    if (indexPath.row == [self.messageArray count] && [self.messageArray count] > 0) {
        //load more view cell
        //loading
        return 48.0f;
    }
    
    TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row];
    if (currentMessage != nil) {
        BOOL isHidden = currentMessage.isHidden;
        if (isHidden) {
            //Set height = 0 for hidden message
            return 0.0f;
        }
    }
    
    if (currentMessage.type == TAPChatMessageTypeProduct) {
        //DV Note - For product list height
        //    Collection view height (347.0f) + 16.0f gap
        return 363.0f;
    }
    else if (currentMessage.type == TAPChatMessageTypeUnreadMessageIdentifier) {
        //Unread message identifier UI
        return 42.0f;
    }
    else {
        tableView.estimatedRowHeight = 70.0f;
        return UITableViewAutomaticDimension;
    }
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row < [self.messageArray count]) {
        TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row];
        if (currentMessage != nil) {
            BOOL isHidden = currentMessage.isHidden;
            if (isHidden) {
                //Set height = 0 for hidden message
                return 0.0f;
            }
        }
        
        NSNumber *height = [self.cellHeightsDictionary objectForKey:currentMessage.localID];
        if (height) {
            CGFloat heightFloat = [height doubleValue];
            if (heightFloat < 0.0f) {
                heightFloat = 0.0f;
            }
            return heightFloat;
        }
        return UITableViewAutomaticDimension;
    }
    
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return FLT_MIN;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
    return view;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return FLT_MIN;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
    return view;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    if ([self.messageArray count] != 0) {
        if (indexPath.row == [self.messageArray count]) {
            //load more view cell
            //loading
            [tableView registerNib:[TAPLoadingTableViewCell cellNib] forCellReuseIdentifier:[TAPLoadingTableViewCell description]];
            TAPLoadingTableViewCell *cell = (TAPLoadingTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPLoadingTableViewCell description] forIndexPath:indexPath];
            [cell animateLoading:YES];
            return cell;
        }
        
        TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row];
        
        //Check user is equal to current user
        if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
            //My Chat
            if (message.isDeleted) {
                //Deleted Message (My Chat)
                [tableView registerNib:[TAPMyChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyChatDeletedBubbleTableViewCell description]];
                TAPMyChatDeletedBubbleTableViewCell *cell = (TAPMyChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyChatDeletedBubbleTableViewCell description] forIndexPath:indexPath];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                cell.tag = indexPath.row;
                cell.userInteractionEnabled = YES;
                cell.contentView.userInteractionEnabled = YES;
                cell.delegate = self;
                [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message];
                
                return cell;
            }
            else {
                if (message.type == TAPChatMessageTypeText) {
                    //My Chat Text Message
                    [tableView registerNib:[TAPMyChatBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyChatBubbleTableViewCell description]];
                    TAPMyChatBubbleTableViewCell *cell = (TAPMyChatBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyChatBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) {
                        [cell showStatusLabel:YES animated:NO updateStatusIcon:NO message:message];
                    }
                    else {
                        [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message];
                    }
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeImage) {
                    //My Chat Image Message
                    [tableView registerNib:[TAPMyImageBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyImageBubbleTableViewCell description]];
                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyImageBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    [cell showStatusLabel:YES];
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message.isFailedSend) {
                        //Update view to failed send
                        
                        // Fetch image data, get from cache or download if needed
                        [self fetchImageDataWithMessage:message];
                        [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeFailed];
                    }
                    else {
                        NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message];
                        // 0 is not found
                        // 1 is uploading
                        // 2 is waiting for upload
                        if (status != 0) {
                            //Set current progress
                            NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID];
                            if (uploadProgressDictionary == nil) {
                                CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue];
                                CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue];
                                
                                [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeUploading];
                                
                                [cell animateProgressUploadingImageWithProgress:progress total:total];
                            }
                        }
                        else {
                            // Fetch image data, get from cache or download if needed
                            [self fetchImageDataWithMessage:message];
                        }
                    }
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeVideo) {
                    //My Chat Video Message
                    [tableView registerNib:[TAPMyVideoBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyVideoBubbleTableViewCell description]];
                    TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyVideoBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message != nil) {
                        NSDictionary *dataDictionary = message.data;
                        NSString *fileID = [dataDictionary objectForKey:@"fileID"];
                        NSString *localID = message.localID;
                        NSString *roomID = message.room.roomID;
                        
                        if (message.isFailedSend) {
                            //Update view to failed send
                            [cell animateFailedUploadVideo];
                        }
                        else {
                            NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message];
                            // 0 is not found
                            // 1 is uploading
                            // 2 is waiting for upload
                            if (status != 0) {
                                //Set current progress
                                NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID];
                                if (uploadProgressDictionary == nil) {
                                    CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue];
                                    CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue];
                                    
                                    [cell showVideoBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading];
                                    [cell animateProgressUploadingVideoWithProgress:progress total:total];
                                }
                            }
                            else {
                                //Check video is done downloaded or not
                                NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
                                
                                if ([filePath isEqualToString:@""] || filePath == nil) {
                                    //File not exist, download file
                                    if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) {
                                        //previous download fail, show retry
                                        [cell showVideoBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeRetryDownload];
                                    }
                                    else {
                                        //show download
                                        [cell showDownloadedState:NO];
                                        [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeNotDownloaded];
                                    }
                                }
                                else {
                                    //File exist, show downloaded file
                                    [cell showDownloadedState:YES];
                                    [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeDoneDownloadedUploaded];
                                }
                            }
                        }
                    }
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeFile) {
                    //My Chat File Message
                    [tableView registerNib:[TAPMyFileBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyFileBubbleTableViewCell description]];
                    TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyFileBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message != nil) {
                        NSDictionary *dataDictionary = message.data;
                        NSString *fileID = [dataDictionary objectForKey:@"fileID"];
                        NSString *localID = message.localID;
                        NSString *roomID = message.room.roomID;
                        
                        if (message.isFailedSend) {
                            //Update view to failed send
                            [cell animateFailedUploadFile];
                        }
                        else {
                            NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message];
                            // 0 is not found
                            // 1 is uploading
                            // 2 is waiting for upload
                            if (status != 0) {
                                //Set current progress
                                NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID];
                                if (uploadProgressDictionary == nil) {
                                    CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue];
                                    CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue];
                                    
                                    [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading];
                                    [cell animateProgressUploadingFileWithProgress:progress total:total];
                                }
                            }
                            else {
                                //Check file is done downloaded or not
                                NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
                                
                                if ([filePath isEqualToString:@""] || filePath == nil) {
                                    //File not exist, download file
                                    if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) {
                                        //previous download fail, show retry
                                        [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeRetryDownload];
                                    }
                                    else {
                                        //show download
                                        [cell showDownloadedState:NO];
                                    }
                                }
                                else {
                                    //File exist, show downloaded file
                                    [cell showDownloadedState:YES];
                                }
                            }
                        }
                    }
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeLocation) {
                    //My Chat Location Message
                    [tableView registerNib:[TAPMyLocationBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyLocationBubbleTableViewCell description]];
                    TAPMyLocationBubbleTableViewCell *cell = (TAPMyLocationBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyLocationBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message.isFailedSend) {
                        [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message];
                    }
                    else {
                        [cell showStatusLabel:YES animated:NO updateStatusIcon:NO message:message];
                    }
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeProduct) {
                    //Product Message
                    [tableView registerNib:[TAPProductListBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPProductListBubbleTableViewCell description]];
                    TAPProductListBubbleTableViewCell *cell = (TAPProductListBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPProductListBubbleTableViewCell description] forIndexPath:indexPath];
                    NSArray *productListArray = [message.data objectForKey:@"items"];
                    [cell setProductListBubbleCellWithData:productListArray];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeSingleOption];
                    cell.isCurrentActiveUserProduct = YES;
                    cell.delegate = self;
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeUnreadMessageIdentifier) {
                    //Unread Identifier
                    [tableView registerNib:[TAPUnreadMessagesBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPUnreadMessagesBubbleTableViewCell description]];
                    TAPUnreadMessagesBubbleTableViewCell *cell = (TAPUnreadMessagesBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPUnreadMessagesBubbleTableViewCell description] forIndexPath:indexPath];
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeSystemMessage) {
                    //System Message
                    [tableView registerNib:[TAPSystemMessageTableViewCell cellNib] forCellReuseIdentifier:[TAPSystemMessageTableViewCell description]];
                    TAPSystemMessageTableViewCell *cell = (TAPSystemMessageTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPSystemMessageTableViewCell description] forIndexPath:indexPath];
                    [cell setMessage:message];
                    return cell;
                }
                else {
                    //check if custom bubble available
                    NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type];
                    
                    if([cellDataDictionary count] > 0 && cellDataDictionary != nil) {
                        //if custom bubble from client available
                        NSString *cellName = [cellDataDictionary objectForKey:@"name"];
                        id userDelegate = [cellDataDictionary objectForKey:@"delegate"];
                        
                        UINib *cellNib = [UINib nibWithNibName:cellName bundle:[NSBundle mainBundle]];
                        [tableView registerNib:cellNib forCellReuseIdentifier:cellName];
                        
                        TAPBaseGeneralBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName forIndexPath:indexPath];
                        cell.delegate = userDelegate;
                        cell.clipsToBounds = YES;
                        if (!message.isHidden) {
                            [cell setMessage:message];
                        }
                        return cell;
                    }
                }
            }
        }
        else {
            //Their Chat
            if (message.isDeleted) {
                //Deleted Message (Their Chat)
                [tableView registerNib:[TAPYourChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourChatDeletedBubbleTableViewCell description]];
                TAPYourChatDeletedBubbleTableViewCell *cell = (TAPYourChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourChatDeletedBubbleTableViewCell description] forIndexPath:indexPath];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                cell.tag = indexPath.row;
                cell.userInteractionEnabled = YES;
                cell.contentView.userInteractionEnabled = YES;
                cell.delegate = self;
                [cell setMessage:message];
                [cell showStatusLabel:NO animated:NO];
                
                return cell;
            }
            else {
                if (message.type == TAPChatMessageTypeText) {
                    
                    //Their Chat Message
                    [tableView registerNib:[TAPYourChatBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourChatBubbleTableViewCell description]];
                    TAPYourChatBubbleTableViewCell *cell = (TAPYourChatBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourChatBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) {
                        [cell showStatusLabel:YES animated:NO];
                    }
                    else {
                        [cell showStatusLabel:NO animated:NO];
                    }
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeImage) {
                    //Their Image Message
                    [tableView registerNib:[TAPYourImageBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourImageBubbleTableViewCell description]];
                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourImageBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    [cell showStatusLabel:YES animated:NO];
                    
                    NSDictionary *progressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID];
                    if (progressDictionary != nil) {
                        CGFloat progress = [[progressDictionary objectForKey:@"progress"] floatValue];
                        CGFloat total = [[progressDictionary objectForKey:@"total"] floatValue];
                        [cell setInitialAnimateDownloadingImage];
                        [cell animateProgressDownloadingImageWithProgress:progress total:total];
                    }
                    else {
                        //Fetch image data, get from cache or download if needed
                        [self fetchImageDataWithMessage:message];
                    }
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeVideo) {
                    //Their Video Message
                    [tableView registerNib:[TAPYourVideoBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourVideoBubbleTableViewCell description]];
                    TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourVideoBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message != nil) {
                        NSDictionary *dataDictionary = message.data;
                        NSString *fileID = [dataDictionary objectForKey:@"fileID"];
                        NSString *localID = message.localID;
                        NSString *roomID = message.room.roomID;
                        
                        //Check video is done downloaded or not
                        NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
                        
                        if ([filePath isEqualToString:@""] || filePath == nil) {
                            //File not exist, download file
                            if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) {
                                //previous download fail, show retry
                                [cell showVideoBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeRetry];
                            }
                            else {
                                //show download
                                [cell showDownloadedState:NO];
                                [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeNotDownloaded];
                            }
                        }
                        else {
                            //File exist, show downloaded file
                            [cell showDownloadedState:YES];
                            [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeDoneDownloaded];
                        }
                    }
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeFile) {
                    //Their File Message
                    [tableView registerNib:[TAPYourFileBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourFileBubbleTableViewCell description]];
                    TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourFileBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.tag = indexPath.row;
                    cell.userInteractionEnabled = YES;
                    cell.contentView.userInteractionEnabled = YES;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    if (message != nil) {
                        NSDictionary *dataDictionary = message.data;
                        NSString *fileID = [dataDictionary objectForKey:@"fileID"];
                        NSString *localID = message.localID;
                        NSString *roomID = message.room.roomID;
                        
                        //Check file is done downloaded or not
                        NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
                        
                        if ([filePath isEqualToString:@""] || filePath == nil) {
                            //File not exist, download file
                            //File not exist, download file
                            if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) {
                                //previous download fail, show retry
                                [cell showFileBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeRetry];
                            }
                            else {
                                //show download
                                [cell showDownloadedState:NO];
                            }
                        }
                        else {
                            //File exist, show downloaded file
                            [cell showDownloadedState:YES];
                        }
                    }
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeLocation) {
                    //Their Location Message
                    [tableView registerNib:[TAPYourLocationBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourLocationBubbleTableViewCell description]];
                    TAPYourLocationBubbleTableViewCell *cell = (TAPYourLocationBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourLocationBubbleTableViewCell description] forIndexPath:indexPath];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.delegate = self;
                    cell.message = message;
                    
                    if (!message.isHidden) {
                        [cell setMessage:message];
                    }
                    
                    [cell showStatusLabel:YES animated:NO];
                    
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeProduct) {
                    //Product Message
                    [tableView registerNib:[TAPProductListBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPProductListBubbleTableViewCell description]];
                    TAPProductListBubbleTableViewCell *cell = (TAPProductListBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPProductListBubbleTableViewCell description] forIndexPath:indexPath];
                    NSArray *productListArray = [message.data objectForKey:@"items"];
                    [cell setProductListBubbleCellWithData:productListArray];
                    cell.selectionStyle = UITableViewCellSelectionStyleNone;
                    cell.delegate = self;
                    cell.isCurrentActiveUserProduct = NO;
                    [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeTwoOption];
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeUnreadMessageIdentifier) {
                    //Unread Identifier
                    [tableView registerNib:[TAPUnreadMessagesBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPUnreadMessagesBubbleTableViewCell description]];
                    TAPUnreadMessagesBubbleTableViewCell *cell = (TAPUnreadMessagesBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPUnreadMessagesBubbleTableViewCell description] forIndexPath:indexPath];
                    return cell;
                }
                else if (message.type == TAPChatMessageTypeSystemMessage) {
                    //System Message
                    [tableView registerNib:[TAPSystemMessageTableViewCell cellNib] forCellReuseIdentifier:[TAPSystemMessageTableViewCell description]];
                    TAPSystemMessageTableViewCell *cell = (TAPSystemMessageTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPSystemMessageTableViewCell description] forIndexPath:indexPath];
                    [cell setMessage:message];
                    return cell;
                }
                else {
                    //check if custom bubble available
                    NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type];
                    
                    if([cellDataDictionary count] > 0 && cellDataDictionary != nil) {
                        //if custom bubble from client available
                        NSString *cellName = [cellDataDictionary objectForKey:@"name"];
                        id userDelegate = [cellDataDictionary objectForKey:@"delegate"];
                        
                        UINib *cellNib = [UINib nibWithNibName:cellName bundle:[NSBundle mainBundle]];
                        [tableView registerNib:cellNib forCellReuseIdentifier:cellName];
                        
                        TAPBaseGeneralBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName forIndexPath:indexPath];
                        cell.delegate = userDelegate;
                        cell.clipsToBounds = YES;
                        if (!message.isHidden) {
                            [cell setMessage:message];
                        }
                        return cell;
                    }
                }
            }
        }
    }
    
    //    [tableView registerNib:[TAPBaseXIBRotatedTableViewCell cellNib] forCellReuseIdentifier:[TAPBaseXIBRotatedTableViewCell description]];
    //    TAPBaseXIBRotatedTableViewCell *cell = (TAPBaseXIBRotatedTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPBaseXIBRotatedTableViewCell description] forIndexPath:indexPath];
    //    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    UITableViewCell *cell = [[UITableViewCell alloc] init];
    cell.backgroundColor = [TAPUtil getColor:@"F3F3F3"];
    return cell;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.messageArray count] == 0 || indexPath.row >= [self.messageArray count]) {
        //reject when out of message bounds
        return;
    }
    
    //Process message as Read, remove local notification and call API read
    TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row];
    if (![message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
        //Their chat
        [self processMessageAsRead:message forceMarkAsRead:NO];
    }
    
    //Check and remove unread count message array
    if ([self.anchorUnreadMessageArray count] > 0) {
        TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row];
        [self removeMessageFromAnchorUnreadArray:message];
    }
    
    //Retreive before message
    if (indexPath.row == [self.messageArray count] - 5 && !self.isLoadingOldMessageFromAPI) {
        [self retrieveExistingMessages];
    }
    
    //save cell height to prevent jumpy effects
    TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row];
    if (currentMessage != nil) {
        BOOL isHidden = currentMessage.isHidden;
        if (isHidden) {
            //Set height = 0 for hidden message
            [self.cellHeightsDictionary setObject:@(0.0f) forKey:currentMessage.localID];
        }
        else {
            //save cell height to prevent jumpy effects
            if (cell.frame.size.height >= 0.0f) {
                [self.cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:currentMessage.localID];
            }
        }
    }
}

#pragma mark QLPreviewController
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller {
    return 1;
}

- (id <QLPreviewItem>) previewController:(QLPreviewController *)controller previewItemAtIndex: (NSInteger) index {
    return self.currentSelectedFileURL;
}

#pragma mark - Delegate
#pragma mark UITableView
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
}

#pragma mark UIScrollView
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _isScrollViewDragged = YES;
    
    //Hide unread message indicator top view
    if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) {
        [TAPUtil performBlock:^{
            [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
        } afterDelay:1.0f];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    _isScrollViewDragged = NO;
    
    //move chat anchor button position to default position according to keyboard height
    [UIView animateWithDuration:0.2f animations:^{
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        
        CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight;
        
        self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
        
        [self.view layoutIfNeeded];
        
        if (tableViewYContentInset <= self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight) {
            //set keyboard state to default
            [self setKeyboardStateDefault];
        }
    }];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.contentOffset.y > kShowChatAnchorOffset) {
        if (self.chatAnchorBackgroundView.alpha != 1.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorBackgroundView.alpha = 1.0f;
                self.chatAnchorButton.alpha = 1.0f;
            }];
            
            [self checkAnchorUnreadLabel];
        }
    }
    else {
        if (self.chatAnchorBackgroundView.alpha != 0.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorBackgroundView.alpha = 0.0f;
                self.chatAnchorButton.alpha = 0.0f;
                self.chatAnchorBadgeView.alpha = 0.0f;
            }];
        }
        
        //Check scrolled pending array
        if (!self.isOnScrollPendingChecking) {
            _isOnScrollPendingChecking = YES;
            
            NSInteger numberOfPendingArray = [self.scrolledPendingMessageArray count];
            
            if (numberOfPendingArray > 0) {
                //Add pending message to messageArray (pending message has previously inserted in messageDictionary in didReceiveNewMessage)
                [self.messageArray insertObjects:self.scrolledPendingMessageArray atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfPendingArray)]];
                
                [self.scrolledPendingMessageArray removeAllObjects];
                [self.tableView reloadData];
                
                [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfPendingArray - 1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
            }
            
            _isOnScrollPendingChecking = NO;
        }
    }
    
    //CS NOTE - move chat anchor button as the keyboard move interactively
    CGPoint positionInView = [scrollView.panGestureRecognizer locationInView:self.view];
    CGFloat keyboardAndAccessoryViewHeight = self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight;
    CGFloat totalKeyboardHeight = self.keyboardHeight; //include inputView height
    CGFloat keyboardMinYPositionInView = CGRectGetHeight([UIScreen mainScreen].bounds) - totalKeyboardHeight;
    
    CGFloat touchYPosition = positionInView.y + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO];
    
    if (self.isKeyboardShowed && touchYPosition >= keyboardMinYPositionInView && self.keyboardHeight != keyboardAndAccessoryViewHeight && self.isScrollViewDragged) {
        CGFloat keyboardHeightDifference = touchYPosition - keyboardMinYPositionInView;
        
        if (keyboardHeightDifference < 0.0f) {
            keyboardHeightDifference = 0.0f;
        }
        
        CGFloat chatAnchorBottomConstraint = kChatAnchorDefaultBottomConstraint + (totalKeyboardHeight - keyboardHeightDifference) - kInputMessageAccessoryViewHeight - self.currentInputAccessoryExtensionHeight;
        
        CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight;
        if (messageViewHeightDifference < 0) {
            messageViewHeightDifference = 0.0f;
        }
        
        if (chatAnchorBottomConstraint < kChatAnchorDefaultBottomConstraint + self.currentInputAccessoryExtensionHeight + self.safeAreaBottomPadding + messageViewHeightDifference) {
            chatAnchorBottomConstraint = kChatAnchorDefaultBottomConstraint + self.currentInputAccessoryExtensionHeight + self.safeAreaBottomPadding + messageViewHeightDifference;
        }
        self.chatAnchorButtonBottomConstrait.constant = chatAnchorBottomConstraint;
        self.chatAnchorBackgroundViewBottomConstrait.constant = chatAnchorBottomConstraint;
    }
}

#pragma mark UINavigationController
- (void)handleNavigationPopGesture:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        [self performSelector:@selector(checkIfNeedCloseRoomAfterDelay) withObject:nil afterDelay:0.5f];
        self.isSwipeGestureEnded = YES;
    }
}

- (void)checkIfNeedCloseRoomAfterDelay {
    if (!self.isViewWillAppeared) {
        [self.lastSeenTimer invalidate];
        _lastSeenTimer = nil;
        [self destroySequence];
    }
}

#pragma mark UIDocumentPicker
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
    
    NSError *error = nil;
    NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    
    [coordinator coordinateReadingItemAtURL:[urls firstObject] options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly error:&error byAccessor:^(NSURL *newURL) {
        
        NSError *err = nil;
        NSNumber *fileSize;
        if(![[urls firstObject] getPromisedItemResourceValue:&fileSize forKey:NSURLFileSizeKey error:&err]) {
            NSLog(@"Failed error: %@", error);
            return;
        } else {
            
            TAPCoreConfigsModel *coreConfigs = [TAPDataManager getCoreConfigs];
            NSNumber *maxFileSize = coreConfigs.chatMediaMaxFileSize;
            NSInteger maxFileSizeInMB = [maxFileSize integerValue] / 1024 / 1024; //Convert to MB
            if ([fileSize doubleValue] > [maxFileSize doubleValue]) {
                //File size is larger than max file size
                NSString *errorMessage = [NSString stringWithFormat:@"Maximum file size is %ld MB.", (long)maxFileSizeInMB];
                [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error File Size Excedeed" title:NSLocalizedString(@"Sorry", @"") detailInformation:NSLocalizedString(errorMessage, @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
                return;
            }
            
            NSString *filePath = [[urls firstObject] absoluteString];
            NSString *encodedFileName = [filePath lastPathComponent];
            NSString *decodedFileName = [encodedFileName stringByRemovingPercentEncoding];
            
            //Get Mimetype
            NSString *fileExtension = [newURL pathExtension];
            NSString *mimeType = [TAPUtil mimeTypeForFileWithExtension:fileExtension];
            NSData *fileData = [NSData dataWithContentsOfURL:newURL];
            
            TAPDataFileModel *dataFile = [TAPDataFileModel new];
            dataFile.fileName = decodedFileName;
            dataFile.mediaType = mimeType;
            dataFile.size = fileSize;
            dataFile.fileData = fileData;
            
#ifdef DEBUG
            NSLog(@"FileName: %@ \nMimeType:%@ \nFileSize: %ld",decodedFileName, mimeType, [fileSize doubleValue]);
#endif
            [[TAPChatManager sharedManager] sentFileMessage:dataFile filePath:filePath];
            
            [TAPUtil performBlock:^{
                if ([self.messageArray count] != 0) {
                    [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
                }
            } afterDelay:0.2f];
        }
    }];
}

- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller {
    
}

#pragma mark TAPChatManager
- (void)chatManagerDidSendNewMessage:(TAPMessageModel *)message {
    [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:0];
    NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView beginUpdates];
    [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
    [self.tableView endUpdates];
    [self.tableView scrollsToTop];
}

//- (void)chatManagerDidAddUnreadMessageIdentifier:(TAPMessageModel *)message indexPosition:(NSInteger)index {
//    //CS NOTE - add delay to add unread message identifier to prevent crash caused by unsynced data and ui
//    [TAPUtil performBlock:^{
//        [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:index];
//        //Add unread message identifier to message array and dictionary with index
//        NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:index inSection:0];
//        [self.tableView beginUpdates];
//        [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationMiddle];
//        [self.tableView endUpdates];
//    } afterDelay:0.2f];
//}

- (void)chatManagerDidReceiveNewMessageInActiveRoom:(TAPMessageModel *)message {
    if (![message.room.roomID isEqualToString:self.currentRoom.roomID]) {
        //If message don't have the same room id, reject message
        return;
    }
    
    [self handleMessageFromSocket:message isUpdatedMessage:NO];
    
    //Check if user remove us from the group while we are inside the chat room, handle the case
    if (message.room.type == RoomTypePersonal && message.room.isDeleted) {
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
    }
    else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/removeParticipant"]) {
        if ([message.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
            //Check if system message with action remove participant and target user is current user
            //show deleted chat room view
            [self.view endEditing:YES];
            [self showDeletedRoomView:YES isGroup:YES];
        }
        //refresh room members by API
        [self checkAndRefreshOnlineStatus];
    }
    else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/addParticipant"]) {
        if ([message.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
            [self.view endEditing:YES];
            [self showDeletedRoomView:NO isGroup:YES isGroupDeleted:NO];
        }
        //refresh room members by API
        [self checkAndRefreshOnlineStatus];
    }
    else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/delete"]) {
        [self.view endEditing:YES];
        
        if (message.room.type == RoomTypePersonal) {
            [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
        }
        else if (message.room.type == RoomTypeGroup) {
            [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES];
        }
    }
}

- (void)chatManagerDidReceiveUpdateMessageInActiveRoom:(TAPMessageModel *)message {
    if (![message.room.roomID isEqualToString:self.currentRoom.roomID]) {
        //If message don't have the same room id, reject message
        return;
    }
    
    [self handleMessageFromSocket:message isUpdatedMessage:YES];
}


- (void)chatManagerDidReceiveOnlineStatus:(TAPOnlineStatusModel *)onlineStatus {
    _onlineStatus = onlineStatus;
    NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f;
    [self updateLastSeenWithTimestamp:currentLastSeen];
}

- (void)chatManagerDidReceiveStartTyping:(TAPTypingModel *)typing {
    NSString *currentRoomID = [TAPChatManager sharedManager].activeRoom.roomID;
    currentRoomID = [TAPUtil nullToEmptyString:currentRoomID];
    
    NSString *typingRoomID = typing.roomID;
    typingRoomID = [TAPUtil nullToEmptyString:typingRoomID];
    
    if ([typingRoomID isEqualToString:currentRoomID]) {
        [self setAsTyping:YES];
    }
}

- (void)chatManagerDidReceiveStopTyping:(TAPTypingModel *)typing {
    NSString *currentRoomID = [TAPChatManager sharedManager].activeRoom.roomID;
    currentRoomID = [TAPUtil nullToEmptyString:currentRoomID];
    
    NSString *typingRoomID = typing.roomID;
    typingRoomID = [TAPUtil nullToEmptyString:typingRoomID];
    
    if ([typingRoomID isEqualToString:currentRoomID]) {
        if (self.currentRoom.type == RoomTypePersonal) {
            [self setAsTyping:NO];
        }
        else {
            [self refreshTypingLabelState];
        }
    }
}

#pragma mark TAPMyChatBubbleTableViewCell
- (void)myChatBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    if (tappedMessage.isFailedSend) {
        NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
        NSString *currentMessageString = tappedMessage.body;
        [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{
            [self.messageArray removeObjectAtIndex:messageIndex];
            [self.messageDictionary removeObjectForKey:tappedMessage.localID];
            NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
            [self.tableView endUpdates];
            [[TAPChatManager sharedManager] sendTextMessage:currentMessageString];
        } failure:^(NSError *error) {
            
        }];
    }
    else if (!tappedMessage.isSending) {
        if (tappedMessage == self.selectedMessage) {
            //select message that had been selected
            self.selectedMessage = nil;
            
            NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
            NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
            
            [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                //animation
                [self.tableView beginUpdates];
                [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                [cell layoutIfNeeded];
                [self.tableView endUpdates];
            } completion:^(BOOL finished) {
                //completion
            }];
        }
        else {
            //select message that had not been selected
            if (self.selectedMessage == nil) {
                //no messages had been selected
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    [self.tableView beginUpdates];
                    [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage];
                    [cell layoutIfNeeded];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
            else {
                //a message had been selected
                NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage];
                NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0];
                
                id previousCell;
                BOOL isMyCell = NO;
                if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                    isMyCell = YES;
                }
                else {
                    previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                }
                
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    if (isMyCell) {
                        [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                    }
                    else {
                        [previousCell showStatusLabel:NO animated:YES];
                    }
                    [previousCell layoutIfNeeded];
                    
                    [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
        }
    }
}

- (void)myChatReplyDidTapped {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    //set selected message to chat field
    NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
    [self setReplyMessageWithMessage:self.selectedMessage];
    [self showInputAccessoryExtensionView:YES];
    
    TAPMessageModel *quotedMessageModel = [self.selectedMessage copy];
    [[TAPChatManager sharedManager] saveToQuotedMessage:self.selectedMessage userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
    
    TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:self.selectedMessage];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } completion:^(BOOL finished) {
        //completion
    }];
}

- (void)myChatQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            
            //check if message is forwarded, do nothing on forwarded message
            if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) {
                [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
            }
            
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)myChatBubbleDidTappedUrl:(NSURL *)url
                  originalString:(NSString *)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)myChatBubbleDidTappedPhoneNumber:(NSString *)phoneNumber
                          originalString:(NSString *)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myChatBubbleLongPressedUrl:(NSURL *)url
                    originalString:(NSString *)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)myChatBubbleLongPressedPhoneNumber:(NSString *)phoneNumber
                            originalString:(NSString *)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myChatBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

#pragma mark TAPMyChatDeletedBubbleTableViewCell
- (void)myChatDeletedBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    if (!tappedMessage.isSending) {
        if (tappedMessage == self.selectedMessage) {
            //select message that had been selected
            self.selectedMessage = nil;
            
            NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
            NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
            
            [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                //animation
                [self.tableView beginUpdates];
                [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                [cell layoutIfNeeded];
                [self.tableView endUpdates];
            } completion:^(BOOL finished) {
                //completion
            }];
        }
        else {
            //select message that had not been selected
            if (self.selectedMessage == nil) {
                //no messages had been selected
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    [self.tableView beginUpdates];
                    [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage];
                    [cell layoutIfNeeded];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
            else {
                //a message had been selected
                NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage];
                NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0];
                
                id previousCell;
                BOOL isMyCell = NO;
                if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                    isMyCell = YES;
                }
                else {
                    previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                }
                
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    if (isMyCell) {
                        [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                    }
                    else {
                        [previousCell showStatusLabel:NO animated:YES];
                    }
                    [previousCell layoutIfNeeded];
                    
                    [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
        }
    }
}


#pragma mark TAPMyImageBubbleTableViewCell
- (void)myImageCancelDidTappedWithMessage:(TAPMessageModel *)message {
    
    //Cancel uploading task
    [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message];
    
    //Remove message from array and dictionary in ChatViewController
    TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID];
    NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage];
    [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID];
    
    //Remove from WaitingUploadDictionary in ChatManager
    [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message];
    
    //Remove message from database
    [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{
        
    } failure:^(NSError *error) {
        
    }];
    
    //Update chat room UI
    NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0];
    [self.tableView beginUpdates];
    [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
    [self.tableView endUpdates];
}

- (void)myImageReplyDidTappedWithMessage:(TAPMessageModel *)message {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [message copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = quotedMessageModel.user.fullname;
    quote.content = quotedMessageModel.body;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)myImageQuoteDidTappedWithMessage:(TAPMessageModel *)message {
    if ((![message.replyTo.messageID isEqualToString:@"0"] && ![message.replyTo.messageID isEqualToString:@""]) && ![message.quote.title isEqualToString:@""] && message.quote != nil && message.replyTo != nil) {
        //reply to exists
        
    }
    else if (![message.quote.title isEqualToString:@""] && message.quote != nil) {
        //quote exists
        if(message.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[message.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)myImageRetryDidTappedWithMessage:(TAPMessageModel *)message {
    NSInteger messageIndex = [self.messageArray indexOfObject:message];
    
    [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{
            [self.messageArray removeObjectAtIndex:messageIndex];
            [self.messageDictionary removeObjectForKey:message.localID];
            NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
            [self.tableView endUpdates];
            
            [TAPImageView imageFromCacheWithKey:message.localID message:message success:^(UIImage *savedImage, TAPMessageModel *resultMessage) {
                
                NSDictionary *dataDictionary = resultMessage.data;
                NSString *currentCaption = [dataDictionary objectForKey:@"caption"];
                currentCaption = [TAPUtil nullToEmptyString:currentCaption];
                
                [[TAPChatManager sharedManager] sendImageMessage:savedImage caption:currentCaption];
            }];
    } failure:^(NSError *error) {
        
    }];
}

- (void)myImageDidTapped:(TAPMyImageBubbleTableViewCell *)myImageBubbleCell {
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
    
    _isShowAccessoryView = NO;
    [self reloadInputViews];
    
    CGFloat bubbleImageViewMinY = CGRectGetMinY(myImageBubbleCell.bubbleImageView.frame);
    
    TAPMediaDetailViewController *mediaDetailViewController = [[TAPMediaDetailViewController alloc] init];
    [mediaDetailViewController setMediaDetailViewControllerType:TAPMediaDetailViewControllerTypeImage];
    mediaDetailViewController.delegate = self;
    mediaDetailViewController.message = myImageBubbleCell.message;
    
    UIImage *cellImage = myImageBubbleCell.bubbleImageView.image;
    NSArray *imageSliderImage = [NSArray array];
    if(cellImage != nil) {
        imageSliderImage = @[cellImage];
        TAPMessageModel *currentMessage = myImageBubbleCell.message;
        NSString *cellImageURLString = [TAPUtil nullToEmptyString:[myImageBubbleCell.message.data objectForKey:@"fileID"]];
        
        NSString *fileID = [myImageBubbleCell.message.data objectForKey:@"fileID"];
        fileID = [TAPUtil nullToEmptyString:fileID];
        
        [mediaDetailViewController setThumbnailImageArray:imageSliderImage];
        [mediaDetailViewController setImageArray:@[cellImage]];
        
        [mediaDetailViewController setActiveIndex:0];
        
        NSInteger selectedRow = [self.messageArray indexOfObject:myImageBubbleCell.message];
        NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:selectedRow inSection:0];
        CGRect cellRectInTableView = [self.tableView rectForRowAtIndexPath:selectedIndexPath];
        CGRect cellRectInView = [self.tableView convertRect:cellRectInTableView toView:self.view];
        CGRect imageRectInView = CGRectMake(CGRectGetWidth([UIScreen mainScreen].bounds) - 16.0f - myImageBubbleCell.bubbleImageViewWidthConstraint.constant, CGRectGetMinY(cellRectInView) + bubbleImageViewMinY + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO], myImageBubbleCell.bubbleImageViewWidthConstraint.constant, myImageBubbleCell.bubbleImageViewHeightConstraint.constant);
        
        [mediaDetailViewController showToViewController:self.navigationController thumbnailImage:cellImage thumbnailFrame:imageRectInView];
        myImageBubbleCell.bubbleImageView.alpha = 0.0f;
        _openedBubbleCell = myImageBubbleCell;
    }
}

- (void)myImageDidTappedUrl:(NSURL *)url
             originalString:(NSString *)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)myImageDidTappedPhoneNumber:(NSString *)phoneNumber
                     originalString:(NSString *)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myImageLongPressedUrl:(NSURL *)url
               originalString:(NSString *)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)myImageLongPressedPhoneNumber:(NSString *)phoneNumber
                       originalString:(NSString *)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myImageBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

#pragma mark TAPMyFileBubbleTableViewCell
- (void)myFileQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            //check if message is forwarded, do nothing on forwarded message
            if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) {
                [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
            }
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)myFileReplyDidTapped:(TAPMessageModel *)tappedMessage {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [tappedMessage copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"];
    fileName = [TAPUtil nullToEmptyString:fileName];
    
    NSString *fileExtension  = [[fileName pathExtension] uppercaseString];
    
    fileName = [fileName stringByDeletingPathExtension];
    
    if ([fileExtension isEqualToString:@""]) {
        fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"];
        fileExtension = [TAPUtil nullToEmptyString:fileExtension];
        fileExtension = [fileExtension lastPathComponent];
        fileExtension = [fileExtension uppercaseString];
    }
    
    NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = fileName;
    quote.content = [NSString stringWithFormat:@"%@ %@", fileSize, fileExtension];
    
    NSString *fileTypeString = @"";
    if (quotedMessageModel.type == TAPChatMessageTypeImage) {
        fileTypeString = @"image";
    }
    else if (quotedMessageModel.type == TAPChatMessageTypeVideo) {
        fileTypeString = @"video";
    }
    else if (quotedMessageModel.type == TAPChatMessageTypeFile) {
        fileTypeString = @"file";
    }
    quote.fileType = fileTypeString;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)myFileBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)myFileDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchFileDataWithMessage:tappedMessage];
}

- (void)myFileRetryUploadDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    
    NSDictionary *dataDictionary = tappedMessage.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    
    if ([fileID isEqualToString:@""] || fileID == nil) {
        
        //Remove from waiting upload dictionary in ChatManager
        [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage];
        
        //File exist, retry upload file
        NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
        
        [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{
            
            [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID];
            
            NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
            [self.tableView endUpdates];
            
            NSString *fileName = [tappedMessage.data objectForKey:@"fileName"];
            fileName = [TAPUtil nullToEmptyString:fileName];
            
            NSString *mediaType = [tappedMessage.data objectForKey:@"mediaType"];
            mediaType = [TAPUtil nullToEmptyString:mediaType];
            
            NSString *size = [tappedMessage.data objectForKey:@"size"];
            size = [TAPUtil nullToEmptyString:size];
            
            TAPDataFileModel *dataFile = [TAPDataFileModel new];
            dataFile.fileName = fileName;
            dataFile.mediaType = mediaType;
            dataFile.size = size;
            
            NSString *filePath = [dataDictionary objectForKey:@"filePath"];
            filePath = [TAPUtil nullToEmptyString:filePath];
            
            NSURL *newURL = [NSURL URLWithString:filePath];
            NSData *fileData = [NSData dataWithContentsOfURL:newURL];
            dataFile.fileData = fileData;
            
            [[TAPChatManager sharedManager] sentFileMessage:dataFile filePath:filePath];
            
        } failure:^(NSError *error) {
            
        }];
    }
    else {
        //File not exist, retry download file
        [self fetchFileDataWithMessage:tappedMessage];
    }
}

- (void)myFileCancelButtonDidTapped:(TAPMessageModel *)tappedMessage {
    
    NSDictionary *dataDictionary = tappedMessage.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    
    if ([fileID isEqualToString:@""] || fileID == nil) {
        //File exist, uploading file state
        //Cancel uploading task
        [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:tappedMessage];
        
        //Remove message from array and dictionary in ChatViewController
        TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:tappedMessage.localID];
        NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage];
        [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID];
        
        //Remove from WaitingUploadDictionary in ChatManager
        [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage];
        
        //Remove message from database
        [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{
            
        } failure:^(NSError *error) {
            
        }];
        
        //Update chat room UI
        NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0];
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
        [self.tableView endUpdates];
    }
    else {
        //File not exist, download file
        //Cancel downloading task
        [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage];
    }
}

- (void)myFileOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage {
    
    NSDictionary *dataDictionary = tappedMessage.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    NSString *roomID = tappedMessage.room.roomID;
    NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
    self.currentSelectedFileURL = [NSURL fileURLWithPath:filePath];
    
    QLPreviewController *preview = [[QLPreviewController alloc] init];
    preview.dataSource = self;
    preview.delegate = self;
    
    [self presentViewController:preview animated:YES completion:nil];
}

#pragma mark TAPMyLocationBubbleTableViewCell
- (void)myLocationBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    if (tappedMessage.isFailedSend) {
        NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
        
        NSDictionary *dataDictionary = tappedMessage.data;
        dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary];
        
        NSString *currentAddress = [dataDictionary objectForKey:@"address"];
        currentAddress = [TAPUtil nullToEmptyString:currentAddress];
        CGFloat currentLatitude = [[dataDictionary objectForKey:@"latitude"] floatValue];
        CGFloat currentLongitude = [[dataDictionary objectForKey:@"longitude"] floatValue];
        
        [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{
            [self.messageArray removeObjectAtIndex:messageIndex];
            [self.messageDictionary removeObjectForKey:tappedMessage.localID];
            NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
            [self.tableView endUpdates];
            
            [[TAPChatManager sharedManager] sendLocationMessage:currentLatitude longitude:currentLongitude address:currentAddress];
        } failure:^(NSError *error) {
            
        }];
    }
    else if (!tappedMessage.isSending) {
        NSDictionary *dataDictionary = tappedMessage.data;
        dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary];
        
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
        
        UIAlertAction *googleMapsAction = [UIAlertAction
                                           actionWithTitle:@"Open in Google Maps"
                                           style:UIAlertActionStyleDefault
                                           handler:^(UIAlertAction * action) {
                                               [self performSelector:@selector(openLocationInGoogleMaps:) withObject:dataDictionary];
                                           }];
        
        UIAlertAction *appleMapsAction = [UIAlertAction
                                          actionWithTitle:@"Open in Maps"
                                          style:UIAlertActionStyleDefault
                                          handler:^(UIAlertAction * action) {
                                              [self performSelector:@selector(openLocationInAppleMaps:) withObject:dataDictionary];
                                          }];
        
        UIAlertAction *cancelAction = [UIAlertAction
                                       actionWithTitle:@"Cancel"
                                       style:UIAlertActionStyleCancel
                                       handler:^(UIAlertAction * action) {
                                           //Do some thing here
                                           [self showInputAccessoryView];
                                           [self checkKeyboard];
                                       }];
        
        [googleMapsAction setValue:[[UIImage imageNamed:@"TAPIconGoogleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        [appleMapsAction setValue:[[UIImage imageNamed:@"TAPIconAppleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        
        [googleMapsAction setValue:@0 forKey:@"titleTextAlignment"];
        [appleMapsAction setValue:@0 forKey:@"titleTextAlignment"];
        
        UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
        UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
        
        [googleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [appleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
        
        [alertController addAction:googleMapsAction];
        [alertController addAction:appleMapsAction];
        [alertController addAction:cancelAction];
        
        [self presentViewController:alertController animated:YES completion:nil];
    }
}

- (void)myLocationQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)myLocationReplyDidTapped:(TAPMessageModel *)tappedMessage {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
    [self setReplyMessageWithMessage:tappedMessage];
    [self showInputAccessoryExtensionView:YES];
    
    TAPMessageModel *quotedMessageModel = [tappedMessage copy];
    [[TAPChatManager sharedManager] saveToQuotedMessage:tappedMessage userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
    
    TAPMyLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage];
        //        [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } completion:^(BOOL finished) {
        //completion
    }];
}

- (void)myLocationBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

#pragma mark TAPMyVideoBubbleTableViewCell
- (void)myVideoQuoteDidTappedWithMessage:(TAPMessageModel *)message {
    if ((![message.replyTo.messageID isEqualToString:@"0"] && ![message.replyTo.messageID isEqualToString:@""]) && ![message.quote.title isEqualToString:@""] && message.quote != nil && message.replyTo != nil) {
        //reply to exists
        
    }
    else if (![message.quote.title isEqualToString:@""] && message.quote != nil) {
        //quote exists
        if(message.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[message.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)myVideoReplyDidTappedWithMessage:(TAPMessageModel *)message {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [message copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = quotedMessageModel.user.fullname;
    quote.content = quotedMessageModel.body;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)myVideoBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)myVideoLongPressedUrl:(NSURL *)url
               originalString:(NSString*)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)myVideoLongPressedPhoneNumber:(NSString *)phoneNumber
                       originalString:(NSString *)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myVideoDidTappedUrl:(NSURL *)url
             originalString:(NSString*)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)myVideoDidTappedPhoneNumber:(NSString *)phoneNumber
                     originalString:(NSString*)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)myVideoCancelDidTappedWithMessage:(TAPMessageModel *)message {
    NSDictionary *dataDictionary = message.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    
    if ([fileID isEqualToString:@""] || fileID == nil) {
        //Video exist, uploading file state
        //Cancel uploading task
        [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message];
        
        //Remove message from array and dictionary in ChatViewController
        TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID];
        NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage];
        [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID];
        
        //Remove from WaitingUploadDictionary in ChatManager
        [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message];
        
        //Remove message from database
        [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{
            
        } failure:^(NSError *error) {
            
        }];
        
        //Update chat room UI
        NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0];
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
        [self.tableView endUpdates];
    }
    else {
        //Video not exist, download file
        //Cancel downloading task
        [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:message];
    }
}

- (void)myVideoRetryUploadDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    NSDictionary *dataDictionary = tappedMessage.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    
    if ([fileID isEqualToString:@""] || fileID == nil) {
        //Video exist, retry upload
        NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
        
        [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{

            [self.messageArray removeObjectAtIndex:messageIndex];
            [self.messageDictionary removeObjectForKey:tappedMessage.localID];
            NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
            [self.tableView endUpdates];
            
            NSString *thumbnailImageBase64String = [tappedMessage.data objectForKey:@"thumbnail"];
            NSData *thumbnailImageData = [[NSData alloc] initWithBase64EncodedString:thumbnailImageBase64String options:NSDataBase64DecodingIgnoreUnknownCharacters];
            
            //            PHAsset *asset = [tappedMessage.data objectForKey:@"asset"];
            NSString *assetIdentifier = [tappedMessage.data objectForKey:@"assetIdentifier"];
            PHAsset *asset = [[TAPFileUploadManager sharedManager] getAssetFromPendingUploadAssetDictionaryWithAssetIdentifier:assetIdentifier];
            NSString *caption = [tappedMessage.data objectForKey:@"caption"];
            caption = [TAPUtil nullToEmptyString:caption];
            
            if (asset.mediaType == PHAssetMediaTypeVideo) {
                [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData];
            }
            
        } failure:^(NSError *error) {
            
        }];
    }
    else {
        //Video not exist, retry download
        [self fetchVideoDataWithMessage:tappedMessage];
    }
}

- (void)myVideoDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchVideoDataWithMessage:tappedMessage];
}

- (void)myVideoPlayDidTappedWithMessage:(TAPMessageModel *)message {
    NSDictionary *dataDictionary = message.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    fileID = [TAPUtil nullToEmptyString:fileID];
    
    if (![fileID isEqualToString:@""]) {
        NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:fileID];
        NSURL *url = [NSURL fileURLWithPath:filePath];
        AVAsset *asset = [AVAsset assetWithURL:url];
        
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
        
        //        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
        AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
        AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item];
        
        AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
        controller.delegate = self;
        controller.showsPlaybackControls = YES;
        [self presentViewController:controller animated:YES completion:nil];
        controller.player = player;
        [player play];
    }
}

#pragma mark TAPYourChatBubbleTableViewCell
- (void)yourChatBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    if (!tappedMessage.isSending) {
        if (tappedMessage == self.selectedMessage) {
            //select message that had been selected
            self.selectedMessage = nil;
            
            NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
            NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
            
            [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                //animation
                [cell showStatusLabel:NO animated:YES];
                [cell layoutIfNeeded];
                [self.tableView beginUpdates];
                [self.tableView endUpdates];
            } completion:^(BOOL finished) {
                //completion
            }];
        }
        else {
            //select message that had not been selected
            if (self.selectedMessage == nil) {
                //no messages had been selected
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    [cell showStatusLabel:YES animated:YES];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
            else {
                //a message had been selected
                NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage];
                NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0];
                
                id previousCell;
                BOOL isMyCell = NO;
                if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                    isMyCell = YES;
                }
                else {
                    previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                }
                
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    if (isMyCell) {
                        [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                    }
                    else {
                        [previousCell showStatusLabel:NO animated:YES];
                    }
                    [previousCell layoutIfNeeded];
                    
                    [cell showStatusLabel:YES animated:YES];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
        }
    }
}

- (void)yourChatReplyDidTapped {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    [self showInputAccessoryView];
    
    //set selected message to chat field
    NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
    [self setReplyMessageWithMessage:self.selectedMessage];
    [self showInputAccessoryExtensionView:YES];
    
    TAPMessageModel *quotedMessageModel = [self.selectedMessage copy];
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
    
    TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        [cell showStatusLabel:NO animated:YES];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } completion:^(BOOL finished) {
        //completion
    }];
}

- (void)yourChatQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            //check if message is forwarded, do nothing on forwarded message
            if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) {
                [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
            }
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)yourChatBubbleDidTappedUrl:(NSURL *)url
                    originalString:(NSString *)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)yourChatBubbleDidTappedPhoneNumber:(NSString *)phoneNumber
                            originalString:(NSString *)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourChatBubbleLongPressedUrl:(NSURL *)url
                      originalString:(NSString *)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)yourChatBubbleLongPressedPhoneNumber:(NSString *)phoneNumber
                              originalString:(NSString *)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourChatBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)yourChatBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}

#pragma mark TAPYourChatDeletedBubbleTableViewCell
- (void)yourChatDeletedBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    if (!tappedMessage.isSending) {
        if (tappedMessage == self.selectedMessage) {
            //select message that had been selected
            self.selectedMessage = nil;
            
            NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
            NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
            TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
            
            [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                //animation
                [cell showStatusLabel:NO animated:YES];
                [cell layoutIfNeeded];
                [self.tableView beginUpdates];
                [self.tableView endUpdates];
            } completion:^(BOOL finished) {
                //completion
            }];
        }
        else {
            //select message that had not been selected
            if (self.selectedMessage == nil) {
                //no messages had been selected
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    [cell showStatusLabel:YES animated:YES];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
            else {
                //a message had been selected
                NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage];
                NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0];
                
                id previousCell;
                BOOL isMyCell = NO;
                if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                    isMyCell = YES;
                }
                else {
                    previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath];
                }
                
                self.selectedMessage = tappedMessage;
                NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
                NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
                
                TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
                
                [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
                    //animation
                    if (isMyCell) {
                        [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage];
                    }
                    else {
                        [previousCell showStatusLabel:NO animated:YES];
                    }
                    [previousCell layoutIfNeeded];
                    
                    [cell showStatusLabel:YES animated:YES];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
        }
    }
}

- (void)yourChatDeletedBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}

#pragma mark TAPYourImageBubbleTableViewCell
- (void)yourImageReplyDidTappedWithMessage:(TAPMessageModel *)message {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [message copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = quotedMessageModel.user.fullname;
    quote.content = quotedMessageModel.body;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)yourImageQuoteDidTappedWithMessage:(TAPMessageModel *)message {
    if ((![message.replyTo.messageID isEqualToString:@"0"] && ![message.replyTo.messageID isEqualToString:@""]) && ![message.quote.title isEqualToString:@""] && message.quote != nil && message.replyTo != nil) {
        //reply to exists
        
    }
    else if (![message.quote.title isEqualToString:@""] && message.quote != nil) {
        //quote exists
        if(message.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[message.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)yourImageDidTapped:(TAPYourImageBubbleTableViewCell *)yourImageBubbleCell {
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
    
    _isShowAccessoryView = NO;
    [self reloadInputViews];
    
    CGFloat bubbleImageViewMinY = CGRectGetMinY(yourImageBubbleCell.bubbleImageView.frame);
    
    TAPMediaDetailViewController *mediaDetailViewController = [[TAPMediaDetailViewController alloc] init];
    [mediaDetailViewController setMediaDetailViewControllerType:TAPMediaDetailViewControllerTypeImage];
    mediaDetailViewController.delegate = self;
    mediaDetailViewController.message = yourImageBubbleCell.message;
    
    UIImage *cellImage = yourImageBubbleCell.bubbleImageView.image;
    NSArray *imageSliderImage = [NSArray array];
    if(cellImage != nil) {
        imageSliderImage = @[cellImage];
        TAPMessageModel *currentMessage = yourImageBubbleCell.message;
        NSString *cellImageURLString = [TAPUtil nullToEmptyString:[yourImageBubbleCell.message.data objectForKey:@"fileID"]];
        
        NSString *fileID = [yourImageBubbleCell.message.data objectForKey:@"fileID"];
        fileID = [TAPUtil nullToEmptyString:fileID];
        
        [mediaDetailViewController setThumbnailImageArray:imageSliderImage];
        [mediaDetailViewController setImageArray:@[cellImage]];
        
        [mediaDetailViewController setActiveIndex:0];
        
        NSInteger selectedRow = [self.messageArray indexOfObject:yourImageBubbleCell.message];
        NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:selectedRow inSection:0];
        CGRect cellRectInTableView = [self.tableView rectForRowAtIndexPath:selectedIndexPath];
        CGRect cellRectInView = [self.tableView convertRect:cellRectInTableView toView:self.view];
        
        //Default left gap for personal chat
        CGFloat xPosition = 16.0f;
        if (currentMessage.room.type == RoomTypeGroup || currentMessage.room.type == RoomTypeChannel) {
            //left gap + image width + gap between image and bubble view
            xPosition = 16.0f + 30.0f + 4.0f;
        }
        
        CGRect imageRectInView = CGRectMake(xPosition, CGRectGetMinY(cellRectInView) + bubbleImageViewMinY + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO], yourImageBubbleCell.bubbleImageViewWidthConstraint.constant, yourImageBubbleCell.bubbleImageViewHeightConstraint.constant);
        
        [mediaDetailViewController showToViewController:self.navigationController thumbnailImage:cellImage thumbnailFrame:imageRectInView];
        yourImageBubbleCell.bubbleImageView.alpha = 0.0f;
        _openedBubbleCell = yourImageBubbleCell;
    }
}

- (void)yourImageDidTappedUrl:(NSURL *)url
               originalString:(NSString *)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)yourImageDidTappedPhoneNumber:(NSString *)phoneNumber
                       originalString:(NSString *)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourImageLongPressedUrl:(NSURL *)url
                 originalString:(NSString *)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)yourImageLongPressedPhoneNumber:(NSString *)phoneNumber
                         originalString:(NSString *)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourImageBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)yourImageBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}


#pragma mark TAPYourFileBubbleTableViewCell
- (void)yourFileBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    
}

- (void)yourFileQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            //check if message is forwarded, do nothing on forwarded message
            if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) {
                [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
            }
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)yourFileReplyDidTapped:(TAPMessageModel *)tappedMessage {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [tappedMessage copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"];
    fileName = [TAPUtil nullToEmptyString:fileName];
    
    NSString *fileExtension  = [[fileName pathExtension] uppercaseString];
    
    fileName = [fileName stringByDeletingPathExtension];
    
    if ([fileExtension isEqualToString:@""]) {
        fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"];
        fileExtension = [TAPUtil nullToEmptyString:fileExtension];
        fileExtension = [fileExtension lastPathComponent];
        fileExtension = [fileExtension uppercaseString];
    }
    
    NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = fileName;
    quote.content = [NSString stringWithFormat:@"%@ %@", fileSize, fileExtension];
    NSString *fileTypeString = @"";
    if (quotedMessageModel.type == TAPChatMessageTypeImage) {
        fileTypeString = @"image";
    }
    else if (quotedMessageModel.type == TAPChatMessageTypeVideo) {
        fileTypeString = @"video";
    }
    else if (quotedMessageModel.type == TAPChatMessageTypeFile) {
        fileTypeString = @"file";
    }
    quote.fileType = fileTypeString;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)yourFileBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)yourFileDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchFileDataWithMessage:tappedMessage];
}

- (void)yourFileRetryDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchFileDataWithMessage:tappedMessage];
}

- (void)yourFileCancelButtonDidTapped:(TAPMessageModel *)tappedMessage {
    //Cancel downloading task
    [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage];
}

- (void)yourFileOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage {
    NSDictionary *dataDictionary = tappedMessage.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    NSString *roomID = tappedMessage.room.roomID;
    NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
    self.currentSelectedFileURL = [NSURL fileURLWithPath:filePath];
    
    QLPreviewController *preview = [[QLPreviewController alloc] init];
    preview.dataSource = self;
    preview.delegate = self;
    
    [self presentViewController:preview animated:YES completion:nil];
}

- (void)yourFileBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}

#pragma mark TAPYourLocationBubbleTableViewCell
- (void)yourLocationBubbleViewDidTapped:(TAPMessageModel *)tappedMessage {
    NSDictionary *dataDictionary = tappedMessage.data;
    dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary];
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *googleMapsAction = [UIAlertAction
                                       actionWithTitle:@"Open in Google Maps"
                                       style:UIAlertActionStyleDefault
                                       handler:^(UIAlertAction * action) {
                                           [self performSelector:@selector(openLocationInGoogleMaps:) withObject:dataDictionary];
                                       }];
    
    UIAlertAction *appleMapsAction = [UIAlertAction
                                      actionWithTitle:@"Open in Maps"
                                      style:UIAlertActionStyleDefault
                                      handler:^(UIAlertAction * action) {
                                          [self performSelector:@selector(openLocationInAppleMaps:) withObject:dataDictionary];
                                      }];
    
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction * action) {
                                       //Do some thing here
                                       [self showInputAccessoryView];
                                       [self checkKeyboard];
                                   }];
    
    [googleMapsAction setValue:[[UIImage imageNamed:@"TAPIconGoogleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [appleMapsAction setValue:[[UIImage imageNamed:@"TAPIconAppleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    [googleMapsAction setValue:@0 forKey:@"titleTextAlignment"];
    [appleMapsAction setValue:@0 forKey:@"titleTextAlignment"];
    
    UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
    UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
    
    [googleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [appleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
    
    [alertController addAction:googleMapsAction];
    [alertController addAction:appleMapsAction];
    [alertController addAction:cancelAction];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

- (void)yourLocationQuoteViewDidTapped:(TAPMessageModel *)tappedMessage {
    if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) {
        //reply to exists
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            //check if no reply message in loading / fetch to handle double tapped on waiting action
            //check if message is forwarded, do nothing on forwarded message
            if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) {
                [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID];
            }
        }
    }
    else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) {
        //quote exists
        if(tappedMessage.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)yourLocationReplyDidTapped:(TAPMessageModel *)tappedMessage {
    
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
    [self setReplyMessageWithMessage:tappedMessage];
    [self showInputAccessoryExtensionView:YES];
    
    TAPMessageModel *quotedMessageModel = [tappedMessage copy];
    [[TAPChatManager sharedManager] saveToQuotedMessage:tappedMessage userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
    
    TAPYourLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        [cell showStatusLabel:YES animated:YES];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } completion:^(BOOL finished) {
        //completion
    }];
    
}

- (void)yourLocationBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)yourLocationBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}

#pragma mark TAPYourVideoBubbleTableViewCell
- (void)yourVideoQuoteDidTappedWithMessage:(TAPMessageModel *)message {
    if ((![message.replyTo.messageID isEqualToString:@"0"] && ![message.replyTo.messageID isEqualToString:@""]) && ![message.quote.title isEqualToString:@""] && message.quote != nil && message.replyTo != nil) {
        //reply to exists
        
    }
    else if (![message.quote.title isEqualToString:@""] && message.quote != nil) {
        //quote exists
        if(message.data) {
            NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[message.data objectForKey:@"userInfo"]];
            id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
            if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) {
                [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary];
            }
        }
    }
}

- (void)yourVideoReplyDidTappedWithMessage:(TAPMessageModel *)message {
    if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) {
        return;
    }
    
    TAPMessageModel *quotedMessageModel = [message copy];
    
    //WK Note : Do reply here later.
    [self showInputAccessoryExtensionView:NO];
    [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
    [self showInputAccessoryExtensionView:YES];
    
    //convert to quote model
    TAPQuoteModel *quote = [TAPQuoteModel new];
    quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
    quote.title = quotedMessageModel.user.fullname;
    quote.content = quotedMessageModel.body;
    [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
    
    quotedMessageModel.quote = quote;
    
    [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
    
    //remove selectedMessage
    self.selectedMessage = nil;
}

- (void)yourVideoBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage {
    [self handleLongPressedWithMessage:longPressedMessage];
}

- (void)yourVideoLongPressedUrl:(NSURL *)url
                 originalString:(NSString*)originalString {
    [self handleLongPressedWithURL:url originalString:originalString];
}

- (void)yourVideoLongPressedPhoneNumber:(NSString *)phoneNumber
                         originalString:(NSString*)originalString {
    [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourVideoDidTappedUrl:(NSURL *)url
               originalString:(NSString*)originalString {
    [self handleTappedWithURL:url originalString:originalString];
}

- (void)yourVideoDidTappedPhoneNumber:(NSString *)phoneNumber
                       originalString:(NSString*)originalString {
    [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString];
}

- (void)yourVideoPlayDidTappedWithMessage:(TAPMessageModel *)message {
    NSDictionary *dataDictionary = message.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    fileID = [TAPUtil nullToEmptyString:fileID];
    
    if (![fileID isEqualToString:@""]) {
        NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:fileID];
        NSURL *url = [NSURL fileURLWithPath:filePath];
        AVAsset *asset = [AVAsset assetWithURL:url];
        
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
        
        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
        AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item];
        
        AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
        controller.delegate = self;
        controller.showsPlaybackControls = YES;
        [self presentViewController:controller animated:YES completion:nil];
        controller.player = player;
        [player play];
    }
}

- (void)yourVideoCancelDidTappedWithMessage:(TAPMessageModel *)message {
    NSDictionary *dataDictionary = message.data;
    NSString *fileID = [dataDictionary objectForKey:@"fileID"];
    
    if ([fileID isEqualToString:@""] || fileID == nil) {
        //Video exist, uploading file state
        //Cancel uploading task
        [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message];
        
        //Remove message from array and dictionary in ChatViewController
        TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID];
        NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage];
        [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID];
        
        //Remove from WaitingUploadDictionary in ChatManager
        [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message];
        
        //Remove message from database
        [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{
            
        } failure:^(NSError *error) {
            
        }];
        
        //Update chat room UI
        NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0];
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
        [self.tableView endUpdates];
    }
    else {
        //Video not exist, download file
        //Cancel downloading task
        [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:message];
    }
}

- (void)yourVideoRetryDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchVideoDataWithMessage:tappedMessage];
}

- (void)yourVideoDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage {
    [self fetchVideoDataWithMessage:tappedMessage];
}

- (void)yourVideoBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage {
    [self openUserProfileFromGroupChatWithMessage:tappedMessage];
}

#pragma mark TAPProductListBubbleTableViewCell
- (void)productListBubbleDidTappedLeftOrSingleOptionWithData:(NSDictionary *)productDictionary isSingleOptionView:(BOOL)isSingleOption {
   
    TAPUserModel *currentUser = [TAPDataManager getActiveUser];
    NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID];
    TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID];

    TAPProductModel *product = [TAPDataManager productModelFromDictionary:productDictionary];
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;

    id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
    if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkProductListBubbleLeftOrSingleButtonTapped:room:recipient:isSingleOption:)]) {
        [tapUIChatRoomDelegate tapTalkProductListBubbleLeftOrSingleButtonTapped:product room:room recipient:otherUser isSingleOption:isSingleOption];
    }
}

- (void)productListBubbleDidTappedRightOptionWithData:(NSDictionary *)productDictionary isSingleOptionView:(BOOL)isSingleOption {
    TAPUserModel *currentUser = [TAPDataManager getActiveUser];
    NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID];
    TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID];
    
    TAPProductModel *product = [TAPDataManager productModelFromDictionary:productDictionary];
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
    
    id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
    if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkProductListBubbleRightButtonTapped:room:recipient:isSingleOption:)]) {
        [tapUIChatRoomDelegate tapTalkProductListBubbleRightButtonTapped:product room:room recipient:otherUser isSingleOption:isSingleOption];
    }
}

#pragma mark TAPGrowingTextView
- (void)growingTextView:(TAPGrowingTextView *)textView shouldChangeHeight:(CGFloat)height {
    self.messageTextViewHeight = height;
    self.messageTextViewHeightConstraint.constant = height;
    self.messageViewHeightConstraint.constant = self.messageTextViewHeight + 16.0f + 4.0f;
    [self.messageTextView layoutIfNeeded];
    [self.inputMessageAccessoryView layoutIfNeeded];
    [self.view layoutIfNeeded];
}

- (void)growingTextViewDidBeginEditing:(TAPGrowingTextView *)textView {
    
    [self setKeyboardStateDefault];
    
    if (textView.text != nil) {
        if (![textView.text isEqualToString:@""]) {
            if(self.isCustomKeyboardAvailable) {
                [UIView animateWithDuration:0.2f animations:^{
                    self.keyboardOptionButtonView.alpha = 0.0f;
                    self.keyboardOptionButton.alpha = 0.0f;
                    self.keyboardOptionButton.userInteractionEnabled = NO;
                    self.messageViewLeftConstraint.constant = -38.0f;
                    self.keyboardOptionViewRightConstraint.constant = -26.0f;
                    [self.messageTextView layoutIfNeeded];
                    [self.inputMessageAccessoryView layoutIfNeeded];
                    [self.view layoutIfNeeded];
                }];
            }
        }
    }
}

- (void)growingTextViewDidStartTyping:(TAPGrowingTextView *)textView {
    [self setSendButtonActive:YES];
    if (self.isCustomKeyboardAvailable) {
        [UIView animateWithDuration:0.2f animations:^{
            self.keyboardOptionButtonView.alpha = 0.0f;
            self.keyboardOptionButton.alpha = 0.0f;
            self.keyboardOptionButton.userInteractionEnabled = NO;
            self.messageViewLeftConstraint.constant = -38.0f;
            self.keyboardOptionViewRightConstraint.constant = -26.0f;
            [self.messageTextView layoutIfNeeded];
            [self.inputMessageAccessoryView layoutIfNeeded];
        }];
    }
    [[TAPChatManager sharedManager] startTyping];
}

- (void)growingTextViewDidStopTyping:(TAPGrowingTextView *)textView {
    [self setSendButtonActive:NO];
    if (self.isCustomKeyboardAvailable) {
        [UIView animateWithDuration:0.2f animations:^{
            self.keyboardOptionButtonView.alpha = 1.0f;
            self.keyboardOptionButton.alpha = 1.0f;
            self.keyboardOptionButton.userInteractionEnabled = YES;
            self.messageViewLeftConstraint.constant = 4.0f;
            self.keyboardOptionViewRightConstraint.constant = 16.0f;
            [self.messageTextView layoutIfNeeded];
            [self.inputMessageAccessoryView layoutIfNeeded];
            [self.view layoutIfNeeded];
        }];
    }
    [[TAPChatManager sharedManager] stopTyping];
}

#pragma mark TAPConnectionStatusViewController
- (void)connectionStatusViewControllerDelegateHeightChange:(CGFloat)height {
//DV Note - v1.0.18
//28 Nov 2019 - Temporary comment to hide connecting, waiting for network, connected state for further changing UI flow
//    self.connectionStatusHeight = height;
//
//    CGFloat currentHeight = height;
//    if (self.connectionStatusHeight == 0.0f && self.loadMoreMessageViewHeight== 0.0f) {
//        currentHeight = 0.0f;
//    }
//    else if (self.connectionStatusHeight > 0.0f) {
//        currentHeight = self.connectionStatusHeight;
//    }
//    else if (self.loadMoreMessageViewHeight > 0.0f) {
//        currentHeight = self.loadMoreMessageViewHeight;
//    }
//
//    [UIView animateWithDuration:0.2f animations:^{
//        //change frame
//        self.tableViewTopConstraint.constant = currentHeight - 50.0f;
//        [self.view layoutIfNeeded];
//    }];
//END DV Note
}

#pragma mark TAPImagePreviewViewController
- (void)imagePreviewDidTapSendButtonWithData:(NSArray *)dataArray {
    
    //hide empty chat
    [UIView animateWithDuration:0.2f animations:^{
        if (self.emptyView.alpha != 0.0f) {
            self.emptyView.alpha = 0.0f;
        }
    }];
    
    for (TAPMediaPreviewModel *mediaPreview in dataArray) {
        PHAsset *asset = mediaPreview.asset;
        NSString *caption = mediaPreview.caption;
        caption = [TAPUtil nullToEmptyString:caption];
        
        if (asset == nil) {
            //Send image using UIImage
            UIImage *image = mediaPreview.image;
            [[TAPChatManager sharedManager] sendImageMessage:image caption:caption];
        }
        else {
            //Send using PHAsset
            UIImage *thumbnailImage = mediaPreview.thumbnailImage;
            NSData *thumbnailImageData = UIImageJPEGRepresentation(thumbnailImage, 1.0f);
            
            if (asset.mediaType == PHAssetMediaTypeImage) {
                [[TAPChatManager sharedManager] sendImageMessageWithPHAsset:asset caption:caption];
            }
            else if (asset.mediaType == PHAssetMediaTypeVideo) {
                [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData];
            }
        }
    }
    
    [TAPUtil performBlock:^{
        if ([self.messageArray count] != 0) {
            [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
        }
    } afterDelay:0.2f];
    
    //check if keyboard was showed
    //CS NOTE- need to add delay to prevent wrong inset because keyboardwillshow did not called if the method called directly
    [self performSelector:@selector(checkKeyboard) withObject:nil afterDelay:0.05f];
}

- (void)imagePreviewCancelButtonDidTapped {
    [self showInputAccessoryView];
    [self checkKeyboard];
}

- (void)imagePreviewDidSendDataAndCompleteDismissView {
    [self showInputAccessoryView];
    [self checkKeyboard];
}

#pragma mark UIImagePickerController
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
    [picker dismissViewControllerAnimated:YES completion:^{
        if ([[info objectForKey:@"UIImagePickerControllerMediaType"] isEqualToString:@"public.image"]) {
            //IMAGE TYPE
            UIImage *selectedImage;
            
            if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
                selectedImage = [info valueForKey:UIImagePickerControllerOriginalImage];
            }
            else if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
                selectedImage = [info valueForKey:UIImagePickerControllerOriginalImage];
            }
            
            [self performSelector:@selector(showImagePreviewControllerWithSelectedImage:) withObject:selectedImage afterDelay:0.3f];
            
        }
    }];
}

#pragma mark TAPPhotoAlbumListViewController
- (void)photoAlbumListViewControllerSelectImageWithDataArray:(NSArray *)dataArray {
    
}

- (void)photoAlbumListViewControllerDidFinishAndSendImageWithDataArray:(NSArray *)dataArray {
    //Handle send image from gallery
    if(self.currentInputAccessoryExtensionHeight > 0.0f) {
        [self showInputAccessoryExtensionView:NO];
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight;
        
        self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
    }
    
    //hide empty chat
    [UIView animateWithDuration:0.2f animations:^{
        if (self.emptyView.alpha != 0.0f) {
            self.emptyView.alpha = 0.0f;
        }
    }];
    
    for (TAPMediaPreviewModel *mediaPreview in dataArray) {
        PHAsset *asset = mediaPreview.asset;
        
        UIImage *thumbnailImage = mediaPreview.thumbnailImage;
        NSData *thumbnailImageData = UIImageJPEGRepresentation(thumbnailImage, 1.0f);
        
        NSString *caption = mediaPreview.caption;
        caption = [TAPUtil nullToEmptyString:caption];
        
        if (asset.mediaType == PHAssetMediaTypeImage) {
            [[TAPChatManager sharedManager] sendImageMessageWithPHAsset:asset caption:caption];
        }
        else if (asset.mediaType == PHAssetMediaTypeVideo) {
            [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData];
        }
    }
    
    [TAPUtil performBlock:^{
        if ([self.messageArray count] != 0) {
            [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
        }
    } afterDelay:0.2f];
    
    //check if keyboard was showed
    //CS NOTE- need to add delay to prevent wrong inset because keyboardwillshow did not called if the method called directly
    [self performSelector:@selector(checkKeyboard) withObject:nil afterDelay:0.05f];
}

#pragma mark TAPMediaDetailViewController
- (void)mediaDetailViewControllerWillStartClosingAnimation {
    //need to reload inputView after presenting another vc on top
    [self showInputAccessoryView];
}

- (void)mediaDetailViewControllerDidFinishClosingAnimation {
    if ([self.openedBubbleCell isKindOfClass:[TAPMyImageBubbleTableViewCell class]]) {
        TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)self.openedBubbleCell;
        cell.bubbleImageView.alpha = 1.0f;
    }
    else if ([self.openedBubbleCell isKindOfClass:[TAPYourImageBubbleTableViewCell class]]) {
        TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)self.openedBubbleCell;
        cell.bubbleImageView.alpha = 1.0f;
    }
}

#pragma mark TAPPickLocationViewController
- (void)pickLocationViewControllerSetLocationWithLatitude:(CGFloat)latitude
                                                longitude:(CGFloat)longitude
                                                  address:(NSString *)address
                                               postalCode:(NSString *)postalCode {
    [[TAPChatManager sharedManager] sendLocationMessage:latitude longitude:longitude address:address];
    if(self.currentInputAccessoryExtensionHeight > 0.0f) {
        [self showInputAccessoryExtensionView:NO];
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight;
        
        self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
    }
    
    [TAPUtil performBlock:^{
        if ([self.messageArray count] != 0) {
            [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
        }
    } afterDelay:0.2f];
}

#pragma mark QLPreviewController
- (BOOL)previewController:(QLPreviewController *)controller shouldOpenURL:(NSURL *)url forPreviewItem:(id <QLPreviewItem>)item {
    return YES;
}

- (void)previewControllerDidDismiss:(QLPreviewController *)controller {
    [self.inputAccessoryView becomeFirstResponder];
}

#pragma mark TAPProfileViewController
- (void)profileViewControllerDidTriggerLeaveOrDeleteGroupWithRoom:(TAPRoomModel *)room {
    if ([self.delegate respondsToSelector:@selector(chatViewControllerDidLeaveOrDeleteGroupWithRoom:)]) {
        [self.delegate chatViewControllerDidLeaveOrDeleteGroupWithRoom:room];
    }
}

#pragma mark - Custom Method
#pragma mark ViewDidLoad Method
- (void)setupNavigationViewData {
    //This method is used to setup the title view of navigation bar, and also bar button view
    
    //Title View
    _titleView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds) - 56.0f - 56.0f, 43.0f)];
    _nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 2.0f, CGRectGetWidth(self.titleView.frame), 22.0f)];
    
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
    
    UIFont *chatRoomNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontChatRoomNameLabel];
    UIColor *chatRoomNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatRoomNameLabel];
    self.nameLabel.text = room.name;
    self.nameLabel.textColor = chatRoomNameLabelColor;
    self.nameLabel.font = chatRoomNameLabelFont;
    self.nameLabel.textAlignment = NSTextAlignmentCenter;
    [self.titleView addSubview:self.nameLabel];
    
    _userStatusView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, (16.0f - 7.0f) / 2.0f + 1.6f, 7.0f, 7.0f)];
    self.userStatusView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconUserStatusActive];
    self.userStatusView.layer.cornerRadius = CGRectGetHeight(self.userStatusView.frame) / 2.0f;
    self.userStatusView.alpha = 0.0f;
    self.userStatusView.clipsToBounds = YES;
    
    UIFont *chatRoomStatusLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontChatRoomStatusLabel];
    UIColor *chatRoomStatusLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatRoomStatusLabel];
    _userStatusLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.userStatusView.frame) + 4.0f, 0.0f, 0.0f, 16.0f)];
    self.userStatusLabel.textColor = chatRoomStatusLabelColor;
    self.userStatusLabel.font = chatRoomStatusLabelFont;
    self.userStatusLabel.textAlignment = NSTextAlignmentCenter;
    [self.userStatusLabel sizeToFit];
    self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f);
    
    CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f;
    _userDescriptionView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f)];
    self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y);
    [self.userDescriptionView addSubview:self.userStatusView];
    [self.userDescriptionView addSubview:self.userStatusLabel];
    [self.titleView addSubview:self.userDescriptionView];
    [self.navigationItem setTitleView:self.titleView];
    
    _userTypingView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), 100.0f, 16.0f)];
    self.userTypingView.backgroundColor = [UIColor clearColor];
    [self.titleView addSubview:self.userTypingView];
    
    UIImageView *typingAnimationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 16.0f, 16.0f)];
    typingAnimationImageView.animationImages = @[[UIImage imageNamed:@"TAPTypingSequence-1" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-2" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-3" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-4" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-5" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-6" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-7" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-8" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-9" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-10" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-11" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-12" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-13" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-14" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-15" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-16" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]];
    typingAnimationImageView.animationDuration = 0.6f;
    typingAnimationImageView.animationRepeatCount = 0.0f;
    [typingAnimationImageView startAnimating];
    [self.userTypingView addSubview:typingAnimationImageView];
    
    _typingLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, 100.0f, 16.0f)];
    self.typingLabel.font = chatRoomStatusLabelFont;
    self.typingLabel.textColor = chatRoomStatusLabelColor;
    self.typingLabel.text = NSLocalizedString(@"typing", @"");
    [self.typingLabel sizeToFit];
    self.typingLabel.frame = CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, CGRectGetWidth(self.typingLabel.frame), 16.0f);
    [self.userTypingView addSubview:self.typingLabel];
    
    self.userTypingView.frame = CGRectMake(CGRectGetMinX(self.userTypingView.frame), CGRectGetMinY(self.userTypingView.frame), CGRectGetMaxX(self.typingLabel.frame), CGRectGetHeight(self.userTypingView.frame));
    self.userTypingView.center = CGPointMake(self.nameLabel.center.x, self.userTypingView.center.y);
    
    [self setAsTyping:NO];
    [self isShowOnlineDotStatus:NO];
    
    //Right Bar Button
    UIView *rightBarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];

    _rightBarInitialNameView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];
    self.rightBarInitialNameView.alpha = 0.0f;
    self.rightBarInitialNameView.layer.cornerRadius = CGRectGetHeight(self.rightBarInitialNameView.frame) / 2.0f;
    self.rightBarInitialNameView.clipsToBounds = YES;
    [rightBarView addSubview:self.rightBarInitialNameView];
    
    UIFont *initialNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRoomAvatarSmallLabel];
    UIColor *initialNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorRoomAvatarSmallLabel];
    _rightBarInitialNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.rightBarInitialNameView.frame), CGRectGetHeight(self.rightBarInitialNameView.frame))];
    self.rightBarInitialNameLabel.font = initialNameLabelFont;
    self.rightBarInitialNameLabel.textColor = initialNameLabelColor;
    self.rightBarInitialNameLabel.textAlignment = NSTextAlignmentCenter;
    [self.rightBarInitialNameView addSubview:self.rightBarInitialNameLabel];
    
    _rightBarImageView = [[TAPImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];
    self.rightBarImageView.layer.cornerRadius = CGRectGetHeight(self.rightBarImageView.frame) / 2.0f;
    self.rightBarImageView.clipsToBounds = YES;
    self.rightBarImageView.contentMode = UIViewContentModeScaleAspectFill;
    [rightBarView addSubview:self.rightBarImageView];
    
    NSString *profileImageURL = room.imageURL.thumbnail;
    if (profileImageURL == nil || [profileImageURL isEqualToString:@""]) {
        BOOL isGroup;
        if (self.currentRoom.type == RoomTypeGroup) {
            isGroup = YES;
        }
        
        self.rightBarInitialNameView.alpha = 1.0f;
        self.rightBarImageView.alpha = 0.0f;
        self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name];
        self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup];
    }
    else {
        self.rightBarInitialNameView.alpha = 0.0f;
        self.rightBarImageView.alpha = 1.0f;
        [self.rightBarImageView setImageWithURLString:profileImageURL];
    }
    
    UIButton *rightBarButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(rightBarView.frame), CGRectGetHeight(rightBarView.frame))];
    [rightBarButton addTarget:self action:@selector(profileImageDidTapped) forControlEvents:UIControlEventTouchUpInside];
    [rightBarView addSubview:rightBarButton];
    
    UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarView];
    [self.navigationItem setRightBarButtonItem:rightBarButtonItem];
    
    //Left Bar Button
    UIImage *buttonImage = [UIImage imageNamed:@"TAPIconBackArrow" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    buttonImage = [buttonImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconNavigationBarBackButton]];
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)];
    [button setImage:buttonImage forState:UIControlStateNormal];
    [button addTarget:self action:@selector(backButtonDidTapped) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
    [self.navigationItem setLeftBarButtonItem:barButtonItem];
    
    self.quoteFileView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconQuotedFileBackground];
    
    self.deletedRoomIconImageView.image = [self.deletedRoomIconImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconDeletedChatRoom]];
    
    self.chatAnchorBackgroundView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottomBackground];
    self.chatAnchorImageView.image = [self.chatAnchorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottom]];
    
    self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomUnreadButton]];
    
    self.attachmentButton.imageView.image = [self.attachmentButton.imageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerAttach]];

}

- (void)checkIsContainQuoteMessage {
    id quotedMessage = [[TAPChatManager sharedManager] getQuotedMessageObjectWithRoomID:self.currentRoom.roomID];
    if (quotedMessage) {
        [self showInputAccessoryExtensionView:YES];
        if ([quotedMessage isKindOfClass:[TAPMessageModel class]]) {
            _isInputAccessoryExtensionShowedFirstTimeOpen = YES;
            TAPMessageModel *quoteMessageModel = (TAPMessageModel *)quotedMessage;
            
            //if reply exists check if image in quote exists
            //if image exists change view to Quote View
            if((quoteMessageModel.quote.fileID && ![quoteMessageModel.quote.fileID isEqualToString:@""]) || (quoteMessageModel.quote.imageURL && ![quoteMessageModel.quote.imageURL isEqualToString:@""])) {
                [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
                [self setQuoteWithQuote:quoteMessageModel.quote userID:quoteMessageModel.user.userID];
            }
            else {
                [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
                [self setReplyMessageWithMessage:quoteMessageModel];
                
                //Set send button to active when forward model is available
                TAPChatManagerQuoteActionType quoteActionType =  [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID];
                if (quoteActionType == TAPChatManagerQuoteActionTypeForward) {
                    [self setSendButtonActive:YES];
                }
            }
        }
        else if ([quotedMessage isKindOfClass:[TAPQuoteModel class]]) {
            TAPQuoteModel *quoteModel = (TAPQuoteModel *)quotedMessage;
            [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
            [self setQuoteWithQuote:quoteModel userID:@""];
        }
    }
    else {
        [self showInputAccessoryExtensionView:NO];
        [self setSendButtonActive:NO];
    }
}

- (void)setupInputAccessoryView {
    //Input Accessory Extension View
    
    //Setup font for composer textview label same as bubble chat body label size
    UIFont *bubbleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRightBubbleMessageBody];
    self.messageTextView.textView.font = bubbleLabelFont;
    self.messageTextView.placeholderLabel.font = bubbleLabelFont;

    self.keyboardOptionButtonView.layer.cornerRadius = CGRectGetHeight(self.keyboardOptionButtonView.frame) / 2.0f;
    self.keyboardOptionButtonView.clipsToBounds = YES;
    
    self.sendButtonView.layer.cornerRadius = CGRectGetHeight(self.sendButtonView.frame) / 2.0f;
    self.sendButtonView.clipsToBounds = YES;
    
    self.inputMessageAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    
    UIImage *closeImage = [UIImage imageNamed:@"TAPIconClose" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    closeImage = [closeImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomCancelQuote]];
    self.inputMessageAccessoryCloseImageView.image = closeImage;
        
    self.replyMessageInnerContainerView.layer.cornerRadius = 4.0f;
    self.quoteImageView.layer.cornerRadius = 4.0f;
    self.quoteImageView.clipsToBounds = YES;
    self.quoteFileView.layer.cornerRadius = CGRectGetHeight(self.quoteImageView.frame)/2.0f;
    
    [self checkIsContainQuoteMessage];
}

- (void)setupDeletedRoomView {
    //Setup Deleted Room View

    self.deleteRoomButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonDestructiveBackground];
    self.deleteRoomButtonView.layer.cornerRadius = 8.0f;
    self.deletedRoomView.clipsToBounds = YES;
    
    UIFont *buttonFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontButtonLabel];
    UIColor *buttonColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorButtonLabel];
    self.deleteRoomButtonLabel.text = NSLocalizedString(@"Delete Chat", @"");
    self.deleteRoomButtonLabel.textAlignment = NSTextAlignmentCenter;
    self.deleteRoomButtonLabel.font = buttonFont;
    self.deleteRoomButtonLabel.textColor = buttonColor;

    self.deleteRoomButtonIconImageView.image = [UIImage imageNamed:@"TAPIconTrash" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    self.deleteRoomButtonIconImageView.image = [self.deleteRoomButtonIconImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIcon]];
    
    self.deleteRoomButtonLoadingImageView.image = [UIImage imageNamed:@"TAPIconLoadingWhite" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    self.deleteRoomButtonLoadingImageView.image = [self.deleteRoomButtonLoadingImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIcon]];
    
    UIFont *deletedChatRoomTitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontDeletedChatRoomInfoTitleLabel];
    UIColor *deletedChatRoomTitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorDeletedChatRoomInfoTitleLabel];
    UIFont *deletedChatRoomContentLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontDeletedChatRoomInfoContentLabel];
    UIColor *deletedChatRoomContentLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorDeletedChatRoomInfoContentLabel];
    //    UIColor *deletedChatRoomIconColor = [[TAPStyleManager sharedManager] getComponentColorForType:]; //DV ICON
    
    self.deletedRoomView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDeletedChatRoomInfoBackground];
    self.deletedRoomTitleLabel.textColor = deletedChatRoomTitleLabelColor;
    self.deletedRoomTitleLabel.font = deletedChatRoomTitleLabelFont;
    self.deletedRoomContentLabel.textColor = deletedChatRoomContentLabelColor;
    self.deletedRoomContentLabel.font = deletedChatRoomContentLabelFont;
    //    [self.deletedRoomIconImageView setImageTintColor:@"deletedChatRoomIconColor"]; //DV ICON
    
    self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight;
}

- (void)showDeletedRoomView:(BOOL)show isGroup:(BOOL)isGroup isGroupDeleted:(BOOL)isGroupDeleted {
    
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
    
    if (isGroup) {
        if (isGroupDeleted) {
            self.deletedRoomContentLabel.text = NSLocalizedString(@"Sorry, this group is unavailable", @"");
        }
        else {
            self.deletedRoomContentLabel.text = NSLocalizedString(@"You are no longer a participant in this group", @"");
        }
    }
    else {
        self.deletedRoomContentLabel.text = NSLocalizedString(@"This user is no longer available", @"");
    }
    
    //Delete button used to delete room
    if (show) {
        
        _isShowAccessoryView = NO;
        [self reloadInputViews];
        
//        if (withDeleteButton) {
//            //74 is button height and padding
//            self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight + 74.0f;
//            self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight + 74.0f;
//        }
//        else {
//            self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight;
//            self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight;
//        }
        
        //74 is button height and padding
        self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight + 74.0f;
        self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight + 74.0f;
        
        self.deletedRoomView.alpha = 1.0f;
    }
    else {
        self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight;
        self.deletedRoomView.alpha = 0.0f;
        [self showInputAccessoryView];
    }
}

- (void)setupKickedGroupView {
    //Setup Kicked Group View
    self.kickedGroupRoomBackgroundView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDefaultBackground];
    
    self.kickedGroupRoomInfoView.layer.cornerRadius = 8.0f;
    self.kickedGroupRoomInfoView.clipsToBounds = YES;
    self.kickedGroupRoomInfoView.backgroundColor = [[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorSystemMessageBackground] colorWithAlphaComponent:0.82f];
    self.kickedGroupRoomInfoView.layer.shadowColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorSystemMessageBackgroundShadow].CGColor;
    self.kickedGroupRoomInfoView.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
    self.kickedGroupRoomInfoView.layer.shadowOpacity = 0.4f;
    self.kickedGroupRoomInfoView.layer.shadowRadius = 4.0f;
    
    UIFont *systemMessageFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontSystemMessageBody];
    UIColor *systemMessageColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorSystemMessageBody];
    self.kickedGroupRoomInfoLabel.textColor = systemMessageColor;
    self.kickedGroupRoomInfoLabel.font = systemMessageFont;
}

- (void)checkAndSetupAddToContactsView {
    //Setup Add to Contacts View
    UIFont *clickableLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontClickableLabel];
    UIColor *clickableLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorClickableLabel];
    UIFont *destructiveClickableLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontClickableDestructiveLabel];
    UIColor *destructiveClickableLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorClickableDestructiveLabel];
    
    self.blockContactLabel.font = destructiveClickableLabelFont;
    self.blockContactLabel.textColor = destructiveClickableLabelColor;
    self.addContactLabel.font = clickableLabelFont;
    self.addContactLabel.textColor = clickableLabelColor;
    self.closeButtonImageView.image = [self.closeButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIconPrimary]];
    
    CGFloat halfViewWidth = (CGRectGetWidth([UIScreen mainScreen].bounds) - 60.0f) / 2.0f;
    //DV Note - 13 Nov 2019 - Temporary set block contact option width to 0 because not ready
    //DV TODO - add block contact here
//    self.2.constant = halfViewWidth;
//    self.addToContactsViewWidthConstraint = halfViewWidth;
    self.blockUserViewWidthConstraint.constant = 0.0f;
    self.addToContactsViewWidthConstraint.constant = halfViewWidth * 2;
    //END DV Note
    self.addToContactsViewHeightConstraint.constant = 0.0f;
    
    if (self.currentRoom.type != RoomTypeGroup) {
        BOOL obtainedState;
        NSDictionary *obtainedStateDictionary = [[NSUserDefaults standardUserDefaults] secureDictionaryForKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY valid:nil];
        if (obtainedStateDictionary != nil && [obtainedStateDictionary count] != 0) {
            NSNumber *obtainedStateNumber = [obtainedStateDictionary objectForKey:self.currentRoom.roomID];
            obtainedState = [obtainedStateNumber boolValue];
        }
        
        if (obtainedState || self.isOtherUserIsContact) {
            return;
        }
        else {
            self.addToContactsViewHeightConstraint.constant = 48.0f;
            //DV Note - 13 Nov 2019 - Temporary set constant to 60.0f to make it center, set to 0.0f when block contact is implemented
            self.addToContactsViewLeftConstraint.constant = 60.0f;
            //END DV Note
            [UIView animateWithDuration:0.2f animations:^{
                self.addToContactContainerView.alpha = 1.0f;
            }];
        }
    }
    
}

- (void)setSendButtonActive:(BOOL)isActive {
    if (isActive) {
        self.sendButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendBackground];
        self.sendButtonImageView.image = [self.sendButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSend]];
        self.sendButton.userInteractionEnabled = YES;
    }
    else {
        self.sendButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendBackgroundInactive];
        self.sendButtonImageView.image = [self.sendButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendInactive]];
        self.sendButton.userInteractionEnabled = NO;
    }
}

#pragma mark Upload Notification
- (void)fileUploadManagerProgressNotification:(NSNotification *)notification {
    NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
    
    TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
    
    NSString *roomID = obtainedMessage.room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    
//    TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
    NSString *currentActiveRoomID = self.currentRoom.roomID;
    currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
    
    if (![roomID isEqualToString:currentActiveRoomID]) {
        return;
    }
    
    NSString *localID = obtainedMessage.localID;
    localID = [TAPUtil nullToEmptyString:localID];
    
    NSString *progressString = [notificationParameterDictionary objectForKey:@"progress"];
    CGFloat progress = [progressString floatValue];
    
    NSString *totalString = [notificationParameterDictionary objectForKey:@"total"];
    CGFloat total = [totalString floatValue];
    
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
    NSArray *messageArray = [self.messageArray copy];
    NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
    
    TAPChatMessageType type = currentMessage.type;
    if (type == TAPChatMessageTypeImage) {
        TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [cell animateProgressUploadingImageWithProgress:progress total:total];
    }
    else if (type == TAPChatMessageTypeFile) {
        TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [cell animateProgressUploadingFileWithProgress:progress total:total];
    }
    else if (type == TAPChatMessageTypeVideo) {
        TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        cell.message = obtainedMessage;
        [cell animateProgressUploadingVideoWithProgress:progress total:total];
    }
}

- (void)fileUploadManagerStartNotification:(NSNotification *)notification {
    NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
    
    TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
    
    NSString *roomID = obtainedMessage.room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    
//    TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
    NSString *currentActiveRoomID = self.currentRoom.roomID;
    currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
    
    if (![roomID isEqualToString:currentActiveRoomID]) {
        return;
    }
    
    NSString *localID = obtainedMessage.localID;
    localID = [TAPUtil nullToEmptyString:localID];
    
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
    NSArray *messageArray = [self.messageArray copy];
    NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
    
    TAPChatMessageType type = currentMessage.type;
    if (type == TAPChatMessageTypeImage) {
        TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        
        [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeUploading];
    }
    else if (type == TAPChatMessageTypeFile) {
        TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [self.tableView beginUpdates];
        [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading];
        [self.tableView endUpdates];
    }
    else if (type == TAPChatMessageTypeVideo) {
        TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        cell.message = obtainedMessage;
        [self.tableView beginUpdates];
        [cell showVideoBubbleStatusWithType:TAPMyVideoBubbleTableViewCellStateTypeUploading];
        [self.tableView endUpdates];
    }
}

- (void)fileUploadManagerFinishNotification:(NSNotification *)notification {
    NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
    
    TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
    
    NSString *roomID = obtainedMessage.room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    
    NSString *currentActiveRoomID = self.currentRoom.roomID;
    currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
    
    if (![roomID isEqualToString:currentActiveRoomID]) {
        return;
    }
    
    NSString *localID = obtainedMessage.localID;
    localID = [TAPUtil nullToEmptyString:localID];
    
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
    NSArray *messageArray = [self.messageArray copy];
    NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
    TAPChatMessageType type = currentMessage.type;
    if (type == TAPChatMessageTypeImage) {
        TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [cell animateFinishedUploadingImage];
      }
    else if (type == TAPChatMessageTypeFile) {
        TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [self.tableView beginUpdates];
        [cell animateFinishedUploadFile];
        [self.tableView endUpdates];
    }
    else if (type == TAPChatMessageTypeVideo) {
        TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        cell.message = obtainedMessage;
        [self.tableView beginUpdates];
        [cell animateFinishedUploadVideo];
        [self.tableView endUpdates];
    }
}

- (void)fileUploadManagerFailureNotification:(NSNotification *)notification {
    NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
    
    TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
    
    NSString *roomID = obtainedMessage.room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    
//    TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
    NSString *currentActiveRoomID = self.currentRoom.roomID;
    currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
    
    if (![roomID isEqualToString:currentActiveRoomID]) {
        return;
    }
    
    NSString *localID = obtainedMessage.localID;
    localID = [TAPUtil nullToEmptyString:localID];
    
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
    NSArray *messageArray = [self.messageArray copy];
    NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
    
    //Update message status to array and dictionary
    currentMessage.isFailedSend = YES;
    currentMessage.isSending = NO;
    
    TAPChatMessageType type = currentMessage.type;
    if (type == TAPChatMessageTypeImage) {
        TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [cell setMessage:currentMessage];
        [self.tableView beginUpdates];
        [cell animateFailedUploadingImage];
        [self.tableView endUpdates];
    }
    else if (type == TAPChatMessageTypeFile) {
        TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        [cell setMessage:currentMessage];
        [self.tableView beginUpdates];
        [cell animateFailedUploadFile];
        [self.tableView endUpdates];
    }
    else if (type == TAPChatMessageTypeVideo) {
        TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
        cell.message = obtainedMessage;
        [cell setMessage:currentMessage];
        [self.tableView beginUpdates];
        [cell animateFailedUploadVideo];
        [self.tableView endUpdates];
    }
}

#pragma mark Download Notification
- (void)fileDownloadManagerProgressNotification:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
        
        TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
        
        NSString *roomID = obtainedMessage.room.roomID;
        roomID = [TAPUtil nullToEmptyString:roomID];
        
//        TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
        NSString *currentActiveRoomID = self.currentRoom.roomID;
        currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
        
        if (![roomID isEqualToString:currentActiveRoomID]) {
            return;
        }
        
        NSString *localID = obtainedMessage.localID;
        localID = [TAPUtil nullToEmptyString:localID];
        
        NSString *progressString = [notificationParameterDictionary objectForKey:@"progress"];
        CGFloat progress = [progressString floatValue];
        
        NSString *totalString = [notificationParameterDictionary objectForKey:@"total"];
        CGFloat total = [totalString floatValue];
        
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        TAPChatMessageType type = currentMessage.type;
        if (type == TAPChatMessageTypeImage) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressUploadingImageWithProgress:progress total:total];
            }
            else {
                //Their Chat
                TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressDownloadingImageWithProgress:progress total:total];
            }
        }
        else if (type == TAPChatMessageTypeFile) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressDownloadingFileWithProgress:progress total:total];
            }
            else {
                //Their Chat
                TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressDownloadingFileWithProgress:progress total:total];
            }
        }
        else if (type == TAPChatMessageTypeVideo) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressDownloadingVideoWithProgress:progress total:total];
                [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:[NSNumber numberWithFloat:progress/total] stateType:TAPMyVideoBubbleTableViewCellStateTypeDownloading];
            }
            else {
                //Their Chat
                TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateProgressDownloadingVideoWithProgress:progress total:total];
                [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:[NSNumber numberWithFloat:progress/total] stateType:TAPYourVideoBubbleTableViewCellStateTypeDownloading];
            }
        }
    });
}

- (void)fileDownloadManagerStartNotification:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
        
        TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
        
        NSString *roomID = obtainedMessage.room.roomID;
        roomID = [TAPUtil nullToEmptyString:roomID];
        
//        TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
        NSString *currentActiveRoomID = self.currentRoom.roomID;
        currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
        
        if (![roomID isEqualToString:currentActiveRoomID]) {
            return;
        }
        
        NSString *localID = obtainedMessage.localID;
        localID = [TAPUtil nullToEmptyString:localID];
        
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        TAPChatMessageType type = currentMessage.type;
        if (type == TAPChatMessageTypeImage) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                
                if (currentMessage.isFailedSend) {
                    [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeFailed];
                }
                else {
                    [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeDownloading];
                }
            }
            else {
                //Their Chat
                TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell setInitialAnimateDownloadingImage];
            }
        }
        else if (type == TAPChatMessageTypeFile) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                
                [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeDownloading];
            }
            else {
                //Their Chat
                TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell showFileBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeDownloading];
            }
        }
        else if (type == TAPChatMessageTypeVideo) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell showVideoBubbleStatusWithType:TAPMyVideoBubbleTableViewCellStateTypeDownloading];
            }
            else {
                //Their Chat
                TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell showVideoBubbleStatusWithType:TAPYourVideoBubbleTableViewCellStateTypeDownloading];
            }
        }
    });
}

- (void)fileDownloadManagerFinishNotification:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
        
        TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
        
        NSString *roomID = obtainedMessage.room.roomID;
        roomID = [TAPUtil nullToEmptyString:roomID];
        
//        TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
        NSString *currentActiveRoomID = self.currentRoom.roomID;
        currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
        
        if (![roomID isEqualToString:currentActiveRoomID]) {
            return;
        }
        
        NSString *localID = obtainedMessage.localID;
        localID = [TAPUtil nullToEmptyString:localID];
        
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        TAPChatMessageType type = currentMessage.type;
        if (type == TAPChatMessageTypeImage) {
            
            UIImage *fullImage = [notificationParameterDictionary objectForKey:@"fullImage"];
            
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                if (fullImage != nil) {
                    [cell setFullImage:fullImage];
                }
                [cell animateFinishedUploadingImage];
            }
            else {
                //Their Chat
                TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                if (fullImage != nil) {
                    [cell setFullImage:fullImage];
                }
                [cell animateFinishedDownloadingImage];
            }
        }
        else if (type == TAPChatMessageTypeFile) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFinishedDownloadFile];
            }
            else {
                //Their Chat
                TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFinishedDownloadFile];
            }
        }
        else if (type == TAPChatMessageTypeVideo) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFinishedDownloadVideo];
                [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeDoneDownloadedUploaded];
                [cell setThumbnailImageForVideoWithMessage:currentMessage];
            }
            else {
                //Their Chat
                TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFinishedDownloadVideo];
                [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeDoneDownloaded];
                [cell setThumbnailImageForVideoWithMessage:currentMessage];
            }
        }
    });
}

- (void)fileDownloadManagerFailureNotification:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
        
        TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"];
        NSError *error = [notificationParameterDictionary objectForKey:@"error"];
        
        NSString *roomID = obtainedMessage.room.roomID;
        roomID = [TAPUtil nullToEmptyString:roomID];
        
//        TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
        NSString *currentActiveRoomID = self.currentRoom.roomID;
        currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
        
        if (![roomID isEqualToString:currentActiveRoomID]) {
            return;
        }
        
        NSString *localID = obtainedMessage.localID;
        localID = [TAPUtil nullToEmptyString:localID];
        
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        TAPChatMessageType type = currentMessage.type;
        if (type == TAPChatMessageTypeImage) {
            if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                //My Chat
                TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFailedUploadingImage];
            }
            else {
                //Their Chat
                TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                [cell animateFailedDownloadingImage];
            }
        }
        else if (type == TAPChatMessageTypeFile) {
            if (error.code == NSURLErrorCancelled) {
                // canceled
                if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    //My Chat
                    TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [self.tableView beginUpdates];
                    [cell animateCancelDownloadFile];
                    [self.tableView endUpdates];
                }
                else {
                    //Their Chat
                    TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [self.tableView beginUpdates];
                    [cell animateCancelDownloadFile];
                    [self.tableView endUpdates];
                }
            } else {
                // failed
                if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    //My Chat
                    TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [cell animateFailedDownloadFile];
                }
                else {
                    //Their Chat
                    TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [cell animateFailedDownloadFile];
                }
            }
            
        }
        else if (type == TAPChatMessageTypeVideo) {
            if (error.code == NSURLErrorCancelled) {
                // canceled
                if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    //My Chat
                    TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [self.tableView beginUpdates];
                    [cell animateCancelDownloadVideo];
                    [self.tableView endUpdates];
                }
                else {
                    //Their Chat
                    TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [self.tableView beginUpdates];
                    [cell animateCancelDownloadVideo];
                    [self.tableView endUpdates];
                }
            } else {
                // failed
                if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                    //My Chat
                    TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [cell animateFailedDownloadVideo];
                }
                else {
                    //Their Chat
                    TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
                    [cell animateFailedDownloadVideo];
                }
            }
        }
    });
}

#pragma mark Profile Notification
- (void)userProfileDidChangeNotification:(NSNotification *)notification {
    NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object];
    TAPUserModel *obtainedUser = [notificationParameterDictionary objectForKey:@"user"];
    TAPRoomModel *obtainedRoom = [notificationParameterDictionary objectForKey:@"room"];
    
    TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:0];
    currentMessage.room = obtainedRoom;
    
    TAPUserModel *currentUser = [TAPDataManager getActiveUser];
    if (![currentUser.userID isEqualToString:obtainedUser.userID]) {
        //update user data in message
        currentMessage.user = obtainedUser;
    }
    
    //upsert to message database
    [TAPDataManager updateOrInsertDatabaseMessageWithData:@[currentMessage] success:^{
        
    } failure:^(NSError *error) {
        
    }];
}

#pragma mark App Lifecycle Notification
- (void)applicationDidBecomeActiveNotification:(NSNotification *)notification {
    [self checkAndRefreshOnlineStatus];
}

#pragma mark Attachment
- (IBAction)attachmentButtonDidTapped:(id)sender {
    
    //Hide unread message indicator top view
    if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) {
        [TAPUtil performBlock:^{
            [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
        } afterDelay:1.0f];
    }
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *documentsAction = [UIAlertAction
                                      actionWithTitle:@"Documents"
                                      style:UIAlertActionStyleDefault
                                      handler:^(UIAlertAction * action) {
                                          [self performSelector:@selector(openFiles) withObject:nil];
                                      }];
    
    UIAlertAction *cameraAction = [UIAlertAction
                                   actionWithTitle:@"Camera"
                                   style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction * action) {
                                       [self performSelector:@selector(openCamera) withObject:nil];
                                   }];
    
    UIAlertAction *galleryAction = [UIAlertAction
                                    actionWithTitle:@"Gallery"
                                    style:UIAlertActionStyleDefault
                                    handler:^(UIAlertAction * action) {
                                        [self performSelector:@selector(openGallery) withObject:nil];
                                    }];
    
    UIAlertAction *locationAction = [UIAlertAction
                                     actionWithTitle:@"Location"
                                     style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction * action) {
                                         [self performSelector:@selector(pickLocation) withObject:nil];
                                     }];
    
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction * action) {
                                       [self showInputAccessoryView];
                                       [self checkKeyboard];
                                   }];
    
    UIImage *documentActionImage = [UIImage imageNamed:@"TAPIconDocuments" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    documentActionImage = [documentActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetDocument]];
    [documentsAction setValue:[documentActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *cameraActionImage = [UIImage imageNamed:@"TAPIconPhoto" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    cameraActionImage = [cameraActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCamera]];
    [cameraAction setValue:[cameraActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *galleryActionImage = [UIImage imageNamed:@"TAPIconGallery" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    galleryActionImage = [galleryActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetGallery]];
    [galleryAction setValue:[galleryActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *locationActionImage = [UIImage imageNamed:@"TAPIconLocation" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    locationActionImage = [locationActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetLocation]];
    [locationAction setValue:[locationActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    [documentsAction setValue:@0 forKey:@"titleTextAlignment"];
    [cameraAction setValue:@0 forKey:@"titleTextAlignment"];
    [galleryAction setValue:@0 forKey:@"titleTextAlignment"];
    [locationAction setValue:@0 forKey:@"titleTextAlignment"];
    
    UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
    UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];

    [documentsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [cameraAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [galleryAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [locationAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
    
    [alertController addAction:documentsAction];
    [alertController addAction:cameraAction];
    [alertController addAction:galleryAction];
    [alertController addAction:locationAction];
    [alertController addAction:cancelAction];
    
    if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) {
        self.isKeyboardWasShowed = YES;
    }
    else {
        self.isKeyboardWasShowed = NO;
    }
    
    [UIView animateWithDuration:0.2f animations:^{
        [self.messageTextView resignFirstResponder];
        [self.secondaryTextField resignFirstResponder];
    } completion:^(BOOL finished) {
        [self presentViewController:alertController animated:YES completion:^{
            //after animation
        }];
    }];
}

- (void)openGallery {
    
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    
    if (status == PHAuthorizationStatusAuthorized) {
        //        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        //        imagePicker.allowsEditing = NO;
        //        imagePicker.delegate = self;
        //        imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        //
        //        [self presentViewController:imagePicker animated:YES completion:^{
        //            //completion
        //        }];
        TAPPhotoAlbumListViewController *photoAlbumListViewController = [[TAPPhotoAlbumListViewController alloc] init];
        [photoAlbumListViewController setPhotoAlbumListViewControllerType:TAPPhotoAlbumListViewControllerTypeDefault];
        photoAlbumListViewController.delegate = self;
        UINavigationController *photoAlbumListNavigationController = [[UINavigationController alloc] initWithRootViewController:photoAlbumListViewController];
        photoAlbumListNavigationController.modalPresentationStyle = UIModalPresentationFullScreen;
        [self presentViewController:photoAlbumListNavigationController animated:YES completion:nil];
    }
    else if (status == PHAuthorizationStatusNotDetermined) {
        //request
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self openGallery];
            });
        }];
    }
    else {
        //No permission. Trying to normally request it
        NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"];
        UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:@"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
        
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
        [alertController addAction:cancelAction];
        
        UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            if (IS_IOS_11_OR_ABOVE) {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil];
            }
            else {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
            }
        }];
        [alertController addAction:settingsAction];
        
        [self presentViewController:alertController animated:YES completion:nil];
    }
}

- (void)openCamera {
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    
    if (status == AVAuthorizationStatusAuthorized) {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.allowsEditing = NO;
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        
        [self presentViewController:imagePicker animated:YES completion:^{
            //completion
        }];
    }
    else if (status == AVAuthorizationStatusNotDetermined) {
        //request
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self openCamera];
            });
        }];
    }
    else {
        //No permission. Trying to normally request it
        NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"];
        UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:@"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
        
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
        [alertController addAction:cancelAction];
        
        UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            if (IS_IOS_11_OR_ABOVE) {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil];
            }
            else {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
            }
        }];
        [alertController addAction:settingsAction];
        
        [self presentViewController:alertController animated:YES completion:nil];
    }
}

- (void)openFiles {
    UIDocumentPickerViewController *documentPickerViewController = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"] inMode:UIDocumentPickerModeImport];
    documentPickerViewController.delegate = self;
    documentPickerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:documentPickerViewController animated:YES completion:^{
        //        if (@available(iOS 11.0, *)) {
        //            documentPickerViewController.allowsMultipleSelection = YES;
        //        }
    }];
}

- (void)pickLocation {
    
    [[TAPLocationManager sharedManager] requestAuthorization];
    
    TAPPickLocationViewController *pickLocationViewController = [[TAPPickLocationViewController alloc] init];
    pickLocationViewController.delegate = self;
    pickLocationViewController.selectedLocationCoordinate = CLLocationCoordinate2DMake(-999, -999);
    UINavigationController *pickLocationNavigationController = [[UINavigationController alloc] initWithRootViewController:pickLocationViewController];
    pickLocationNavigationController.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:pickLocationNavigationController animated:YES completion:nil];
}

- (void)fetchImageDataWithMessage:(TAPMessageModel *)message {
    [[TAPFileDownloadManager sharedManager] receiveImageDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } success:^(UIImage * _Nonnull fullImage, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    }];
}

- (void)fetchFileDataWithMessage:(TAPMessageModel *)message {
    [[TAPFileDownloadManager sharedManager] receiveFileDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } success:^(NSData * _Nonnull fileData, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    }];
}

- (void)fetchVideoDataWithMessage:(TAPMessageModel *)message {
    [[TAPFileDownloadManager sharedManager] receiveVideoDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } success:^(NSData * _Nonnull fileData, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) {
        //Already Handled via Notification
    }];
}

- (void)showImagePreviewControllerWithSelectedImage:(UIImage *)image {
    TAPImagePreviewViewController *imagePreviewViewController = [[TAPImagePreviewViewController alloc] init];
    imagePreviewViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    imagePreviewViewController.delegate = self;
    
    TAPMediaPreviewModel *imagePreview = [TAPMediaPreviewModel new];
    imagePreview.image = image;
    
    [imagePreviewViewController setMediaPreviewDataWithData:imagePreview];
    UINavigationController *imagePreviewNavigationController = [[UINavigationController alloc] initWithRootViewController:imagePreviewViewController];
    imagePreviewNavigationController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    [self.navigationController presentViewController:imagePreviewNavigationController animated:YES completion:nil];
}

- (void)openLocationInGoogleMaps:(NSDictionary *)dataDictionary {
    
    CGFloat latitude = [[dataDictionary objectForKey:@"latitude"] floatValue];
    CGFloat longitude = [[dataDictionary objectForKey:@"longitude"] floatValue];
    NSString *address = [dataDictionary objectForKey:@"address"];
    address = [address stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; //Convert address string format
    
    NSURL *googleMapsURL = [NSURL URLWithString:@"comgooglemaps://"];
    
    if ([[UIApplication sharedApplication] canOpenURL:googleMapsURL]) {
        NSString *urlString = [NSString stringWithFormat:@"comgooglemaps://?center=%f,%f&zoom=14&q=%f,%f",latitude, longitude, latitude, longitude];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
    } else {
        // GoogleMaps is not installed. Launch AppStore to install GoogleMaps app
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://itunes.apple.com/id/app/id585027354"]];
    }
}

- (void)openLocationInAppleMaps:(NSDictionary *)dataDictionary {
    
    CGFloat latitude = [[dataDictionary objectForKey:@"latitude"] floatValue];
    CGFloat longitude = [[dataDictionary objectForKey:@"longitude"] floatValue];
    NSString *address = [dataDictionary objectForKey:@"address"];
    address = [address stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; //Convert address string format
    
    NSURL *appleMapsURL = [NSURL URLWithString:@"maps://"];
    
    if ([[UIApplication sharedApplication] canOpenURL:appleMapsURL]) {
        NSString *urlString = [NSString stringWithFormat:@"maps://?ll=%f,%f&q=%@", latitude, longitude, address];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
    } else {
        NSLog(@"Can't use maps://");
    }
}

#pragma mark Bubble Chat
- (void)handleLongPressedWithURL:(NSURL *)url originalString:(NSString *)originalString {
    [TAPUtil tapticImpactFeedbackGenerator];
    if ([url.scheme isEqualToString:@"mailto"]) {
        //handle email address
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
        
        UIAlertAction *composeAction = [UIAlertAction
                                        actionWithTitle:@"Compose"
                                        style:UIAlertActionStyleDefault
                                        handler:^(UIAlertAction * action) {
                                            [self showInputAccessoryView];
                                            if([[UIApplication sharedApplication] canOpenURL:url]) {
                                                if(IS_IOS_11_OR_ABOVE) {
                                                    [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil];
                                                }
                                                else {
                                                    [[UIApplication sharedApplication] openURL:url];
                                                }
                                            }
                                        }];
        
        UIAlertAction *copyAction = [UIAlertAction
                                     actionWithTitle:@"Copy"
                                     style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction * action) {
                                         [self showInputAccessoryView];
                                         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                                         [pasteboard setString:originalString];
                                     }];
        
        UIAlertAction *cancelAction = [UIAlertAction
                                       actionWithTitle:@"Cancel"
                                       style:UIAlertActionStyleCancel
                                       handler:^(UIAlertAction * action) {
                                           [self showInputAccessoryView];
                                           [self checkKeyboard];
                                       }];
        
        UIImage *composeEmailActionImage = [UIImage imageNamed:@"TAPIconComposeEmail" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
        composeEmailActionImage = [composeEmailActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetComposeEmail]];
        [composeAction setValue:[composeEmailActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        
        UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
        copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]];
        [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        
        [composeAction setValue:@0 forKey:@"titleTextAlignment"];
        [copyAction setValue:@0 forKey:@"titleTextAlignment"];
        
        UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
        UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
        
        [composeAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
        
        [alertController addAction:composeAction];
        [alertController addAction:copyAction];
        [alertController addAction:cancelAction];
        
        if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) {
            self.isKeyboardWasShowed = YES;
        }
        else {
            self.isKeyboardWasShowed = NO;
        }
        
        [UIView animateWithDuration:0.2f animations:^{
            [self.messageTextView resignFirstResponder];
            [self.secondaryTextField resignFirstResponder];
        } completion:^(BOOL finished) {
            [self presentViewController:alertController animated:YES completion:^{
                //after animation
            }];
        }];
    }
    else {
        //handle link
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
        
        UIAlertAction *openAction = [UIAlertAction
                                     actionWithTitle:@"Open"
                                     style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction * action) {
                                         //CS TEMP - temporary open safari
                                         [self showInputAccessoryView];
                                         if([[UIApplication sharedApplication] canOpenURL:url]) {
                                             if(IS_IOS_11_OR_ABOVE) {
                                                 [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil];
                                             }
                                             else {
                                                 [[UIApplication sharedApplication] openURL:url];
                                             }
                                         }
                                     }];
        
        UIAlertAction *copyAction = [UIAlertAction
                                     actionWithTitle:@"Copy"
                                     style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction * action) {
                                         [self showInputAccessoryView];
                                         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                                         [pasteboard setString:originalString];
                                     }];
        
        UIAlertAction *cancelAction = [UIAlertAction
                                       actionWithTitle:@"Cancel"
                                       style:UIAlertActionStyleCancel
                                       handler:^(UIAlertAction * action) {
                                           [self showInputAccessoryView];
                                           [self checkKeyboard];
                                       }];
        
        UIImage *openActionImage = [UIImage imageNamed:@"TAPIconOpen" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
        openActionImage = [openActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetOpen]];
        [openAction setValue:[openActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        
        UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
        copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]];
        [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
        
        [openAction setValue:@0 forKey:@"titleTextAlignment"];
        [copyAction setValue:@0 forKey:@"titleTextAlignment"];
        
        UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
        UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
        [openAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
        [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
        
        [alertController addAction:openAction];
        [alertController addAction:copyAction];
        [alertController addAction:cancelAction];
        
        if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) {
            self.isKeyboardWasShowed = YES;
        }
        else {
            self.isKeyboardWasShowed = NO;
        }
        
        [UIView animateWithDuration:0.2f animations:^{
            [self.messageTextView resignFirstResponder];
            [self.secondaryTextField resignFirstResponder];
        } completion:^(BOOL finished) {
            [self presentViewController:alertController animated:YES completion:^{
                //after animation
            }];
        }];
    }
}

- (void)handleLongPressedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString {
    [TAPUtil tapticImpactFeedbackGenerator];
    //handle number
    phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@" " withString:@""];
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *callAction = [UIAlertAction
                                 actionWithTitle:@"Call Number"
                                 style:UIAlertActionStyleDefault
                                 handler:^(UIAlertAction * action) {
                                     [self showInputAccessoryView];
                                     NSString *stringURL = [NSString stringWithFormat:@"tel:%@", phoneNumber];
                                     if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) {
                                         if(IS_IOS_11_OR_ABOVE) {
                                             [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil];
                                         }
                                         else {
                                             [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]];
                                         }
                                     }
                                 }];
    
    UIAlertAction *smsAction = [UIAlertAction
                                actionWithTitle:@"SMS Number"
                                style:UIAlertActionStyleDefault
                                handler:^(UIAlertAction * action) {
                                    [self showInputAccessoryView];
                                    NSString *stringURL = [NSString stringWithFormat:@"sms:%@", phoneNumber];
                                    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) {
                                        if(IS_IOS_11_OR_ABOVE) {
                                            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil];
                                        }
                                        else {
                                            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]];
                                        }
                                    }
                                }];
    
    UIAlertAction *copyAction = [UIAlertAction
                                 actionWithTitle:@"Copy"
                                 style:UIAlertActionStyleDefault
                                 handler:^(UIAlertAction * action) {
                                     [self showInputAccessoryView];
                                     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                                     [pasteboard setString:phoneNumber];
                                 }];
    
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction * action) {
                                       //Do some thing here
                                       [self showInputAccessoryView];
                                       [self checkKeyboard];
                                   }];
    
    UIImage *callActionImage = [UIImage imageNamed:@"TAPIconCall" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    callActionImage = [callActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCall]];
    [callAction setValue:[callActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];

    UIImage *smsActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    smsActionImage = [smsActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]];
    [smsAction setValue:[smsActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]];
    [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    [callAction setValue:@0 forKey:@"titleTextAlignment"];
    [smsAction setValue:@0 forKey:@"titleTextAlignment"];
    [copyAction setValue:@0 forKey:@"titleTextAlignment"];
    
    UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
    UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
    [callAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [smsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
    
    [alertController addAction:callAction];
    [alertController addAction:smsAction];
    [alertController addAction:copyAction];
    [alertController addAction:cancelAction];
    
    if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) {
        self.isKeyboardWasShowed = YES;
    }
    else {
        self.isKeyboardWasShowed = NO;
    }
    
    [UIView animateWithDuration:0.2f animations:^{
        [self.messageTextView resignFirstResponder];
        [self.secondaryTextField resignFirstResponder];
    } completion:^(BOOL finished) {
        [self presentViewController:alertController animated:YES completion:^{
            //after animation
        }];
    }];
}

- (void)handleTappedWithURL:(NSURL *)url originalString:(NSString *)originalString {
    if ([url.scheme isEqualToString:@"mailto"]) {
        //handle email address
        //open mail app
        if([[UIApplication sharedApplication] canOpenURL:url]) {
            if(IS_IOS_11_OR_ABOVE) {
                [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil];
            }
            else {
                [[UIApplication sharedApplication] openURL:url];
            }
        }
    }
    else {
        //handle link
        //open webview
        if([[UIApplication sharedApplication] canOpenURL:url]) {
            if(IS_IOS_11_OR_ABOVE) {
                [[UIApplication sharedApplication] openURL:url
                                                   options:@{UIApplicationOpenURLOptionUniversalLinksOnly: @YES}
                                         completionHandler:^(BOOL success){
                                             if(!success) {
                                                 // present in app web view, the app is not installed
                                                 TAPWebViewViewController *webViewController = [[TAPWebViewViewController alloc] init];
                                                 webViewController.urlString = url.absoluteString;
                                                 //CS NOTE - add resign first responder before every pushVC to handle keyboard height
                                                 [self.messageTextView resignFirstResponder];
                                                 [self.secondaryTextField resignFirstResponder];
                                                 [self.navigationController pushViewController:webViewController animated:YES];
                                             }
                                         }];
            }
            else {
                [[UIApplication sharedApplication] openURL:url];
            }
        }
    }
}

- (void)handleTappedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString {
    phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSString *stringURL = [NSString stringWithFormat:@"tel:%@", phoneNumber];
    if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) {
        if(IS_IOS_11_OR_ABOVE) {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil];
        }
        else {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]];
        }
    }
}

- (void)handleLongPressedWithMessage:(TAPMessageModel *)message {
    if (message.isDeleted || (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal)) {
        return;
    }
    
    [TAPUtil tapticImpactFeedbackGenerator];
    //handle message long pressed
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *replyAction = [UIAlertAction
                                  actionWithTitle:@"Reply"
                                  style:UIAlertActionStyleDefault
                                  handler:^(UIAlertAction * action) {
                                      //Reply Action Here
                                      
                                      [self showInputAccessoryView];
                                      
                                      if (message.type == TAPChatMessageTypeText) {
                                          [self showInputAccessoryExtensionView:NO];
                                          [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
                                          [self setReplyMessageWithMessage:message];
                                          [self showInputAccessoryExtensionView:YES];
                                          
                                          TAPMessageModel *quotedMessageModel = [message copy];
                                          [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID];
                                      }
                                      else if (message.type == TAPChatMessageTypeImage) {
                                          TAPMessageModel *quotedMessageModel = [message copy];
                                          
                                          [self showInputAccessoryExtensionView:NO];
                                          [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
                                          [self showInputAccessoryExtensionView:YES];
                                          
                                          //convert to quote model
                                          TAPQuoteModel *quote = [TAPQuoteModel new];
                                          quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
                                          quote.title = quotedMessageModel.user.fullname;
                                          quote.content = quotedMessageModel.body;
                                          [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
                                          
                                          quotedMessageModel.quote = quote;
                                          
                                          [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
                                      }
                                      else if (message.type == TAPChatMessageTypeVideo) {
                                          TAPMessageModel *quotedMessageModel = [message copy];
                                          
                                          [self showInputAccessoryExtensionView:NO];
                                          [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
                                          [self showInputAccessoryExtensionView:YES];
                                          
                                          //convert to quote model
                                          TAPQuoteModel *quote = [TAPQuoteModel new];
                                          quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
                                          quote.title = quotedMessageModel.user.fullname;
                                          quote.content = quotedMessageModel.body;
                                          [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
                                          
                                          quotedMessageModel.quote = quote;
                                          
                                          [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
                                      }
                                      else if (message.type == TAPChatMessageTypeLocation) {
                                          [self showInputAccessoryExtensionView:NO];
                                          [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage];
                                          [self setReplyMessageWithMessage:message];
                                          [self showInputAccessoryExtensionView:YES];
                                          
                                          TAPMessageModel *quotedMessageModel = [message copy];
                                          [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID];
                                      }
                                      else if (message.type == TAPChatMessageTypeFile) {
                                          TAPMessageModel *quotedMessageModel = [message copy];
                                          
                                          [self showInputAccessoryExtensionView:NO];
                                          [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote];
                                          [self showInputAccessoryExtensionView:YES];
                                          
                                          NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"];
                                          fileName = [TAPUtil nullToEmptyString:fileName];
                                          
                                          NSString *fileExtension  = [[fileName pathExtension] uppercaseString];
                                          
                                          fileName = [fileName stringByDeletingPathExtension];
                                          
                                          if ([fileExtension isEqualToString:@""]) {
                                              fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"];
                                              fileExtension = [TAPUtil nullToEmptyString:fileExtension];
                                              fileExtension = [fileExtension lastPathComponent];
                                              fileExtension = [fileExtension uppercaseString];
                                          }
                                          
                                          NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary];
                                          
                                          //convert to quote model
                                          TAPQuoteModel *quote = [TAPQuoteModel new];
                                          quote.fileID = [TAPUtil nullToEmptyString:[quotedMessageModel.data objectForKey:@"fileID"]];
                                          quote.title = fileName;
                                          quote.content = [NSString stringWithFormat:@"%@ %@", fileSize, fileExtension];
                                              NSString *fileTypeString = @"";
                                          if (quotedMessageModel.type == TAPChatMessageTypeImage) {
                                              fileTypeString = @"image";
                                          }
                                          else if (quotedMessageModel.type == TAPChatMessageTypeVideo) {
                                              fileTypeString = @"video";
                                          }
                                          else if (quotedMessageModel.type == TAPChatMessageTypeFile) {
                                              fileTypeString = @"file";
                                          }
                                          quote.fileType = fileTypeString;
                                          [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID];
                                          
                                          quotedMessageModel.quote = quote;
                                          
                                          [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID];
                                      }
                                  }];
    
    UIAlertAction *forwardAction = [UIAlertAction
                                    actionWithTitle:@"Forward"
                                    style:UIAlertActionStyleDefault
                                    handler:^(UIAlertAction * action) {
                                        [self showInputAccessoryView];
                                        //Forward Action Here
                                        TAPForwardListViewController *forwardListViewController = [[TAPForwardListViewController alloc] init];
                                        forwardListViewController.currentNavigationController = self.navigationController;
                                        forwardListViewController.forwardedMessage = message;
                                        UINavigationController *forwardListNavigationController = [[UINavigationController alloc] initWithRootViewController:forwardListViewController];
                                        [self presentViewController:forwardListNavigationController animated:YES completion:nil];
                                    }];
    
    UIAlertAction *copyAction = [UIAlertAction
                                 actionWithTitle:@"Copy"
                                 style:UIAlertActionStyleDefault
                                 handler:^(UIAlertAction * action) {
                                     [self showInputAccessoryView];
                                     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                                     if (message.type == TAPChatMessageTypeText) {
                                         [pasteboard setString:message.body];
                                     }
                                 }];
    
    UIAlertAction *saveToGalleryAction = [UIAlertAction
                                          actionWithTitle:@"Save"
                                          style:UIAlertActionStyleDefault
                                          handler:^(UIAlertAction * action) {
                                              [self showInputAccessoryView];
                                              //Save to gallery Action Here
                                              if (message.type == TAPChatMessageTypeImage) {
                                                  //Save image to gallery
                                                  NSString *fileID = [message.data objectForKey:@"fileID"];
                                                  fileID = [TAPUtil nullToEmptyString:fileID];
                                                  if (![fileID isEqualToString:@""]) {
                                                      [TAPImageView imageFromCacheWithKey:fileID success:^(UIImage *savedImage) {
                                                          UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
                                                      }];
                                                  }
                                              }
                                              else if (message.type == TAPChatMessageTypeVideo) {
                                                  //Save video to gallery
                                                  NSString *roomID = message.room.roomID;
                                                  NSString *fileID = [message.data objectForKey:@"fileID"];
                                                  fileID = [TAPUtil nullToEmptyString:fileID];
                                                  if (![fileID isEqualToString:@""]) {
                                                      NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
                                                      if (![filePath isEqualToString:@""] && filePath != nil) {
                                                          //Video done download, save to gallery
                                                          UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);
                                                      }
                                                  }
                                              }
                                          }];
    
    UIAlertAction *deleteMessageAction = [UIAlertAction
                                          actionWithTitle:@"Delete"
                                          style:UIAlertActionStyleDefault
                                          handler:^(UIAlertAction * action) {
                                              [self showInputAccessoryView];
                                              [self showDeleteMessageActionWithMessageArray:@[message.messageID]];
                                          }];
    
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction * action) {
                                       //Do some thing here
                                       [self showInputAccessoryView];
                                       [self checkKeyboard];
                                   }];
    
    UIImage *replyActionImage = [UIImage imageNamed:@"TAPIconReplyChatOrange" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    replyActionImage = [replyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetReply]]; //DV Temp Icon
    [replyAction setValue:[replyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];

    UIImage *forwardActionImage = [UIImage imageNamed:@"TAPIconForward" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    forwardActionImage = [forwardActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetForward]];
    [forwardAction setValue:[forwardActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];

    UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]];
    [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *saveToGalleryActionImage = [UIImage imageNamed:@"TAPIconSaveOrange" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    saveToGalleryActionImage = [saveToGalleryActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetGallery]]; //DV Temp Icon
    [saveToGalleryAction setValue:[saveToGalleryActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    UIImage *deleteMessageActionImage = [UIImage imageNamed:@"TAPIconTrash" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    deleteMessageActionImage = [deleteMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetTrash]];
    [deleteMessageAction setValue:[deleteMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    [replyAction setValue:@0 forKey:@"titleTextAlignment"];
    [forwardAction setValue:@0 forKey:@"titleTextAlignment"];
    [copyAction setValue:@0 forKey:@"titleTextAlignment"];
    [saveToGalleryAction setValue:@0 forKey:@"titleTextAlignment"];
    [deleteMessageAction setValue:@0 forKey:@"titleTextAlignment"];
    
    UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel];
    UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel];
    UIColor *actionSheetDestructiveColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDestructiveLabel];
    
    [replyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [forwardAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [saveToGalleryAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"];
    [deleteMessageAction setValue:actionSheetDestructiveColor forKey:@"titleTextColor"];
    [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"];
    
    [alertController addAction:replyAction];
    
    if (message.type == TAPChatMessageTypeText || message.type == TAPChatMessageTypeLocation) {
        //DV Temp
        //Show forward action for text and location only (temporary)
        [alertController addAction:forwardAction];
    }
    
    if (message.type == TAPChatMessageTypeText) {
        //Show copy action for chat type text only
        [alertController addAction:copyAction];
    }
    
    if (message.type == TAPChatMessageTypeImage) {
        //check already downloaded or not
        NSString *roomID = message.room.roomID;
        NSString *fileID = [message.data objectForKey:@"fileID"];
        fileID = [TAPUtil nullToEmptyString:fileID];
        if (![fileID isEqualToString:@""]) {
            UIImage *savedImage = nil;
            savedImage = [TAPImageView imageFromCacheWithKey:fileID];
            if (savedImage != nil) {
                //Image exist
                [alertController addAction:saveToGalleryAction];
            }
        }
    }
    
    if (message.type == TAPChatMessageTypeVideo) {
        //check already downloaded or not
        NSString *roomID = message.room.roomID;
        NSString *fileID = [message.data objectForKey:@"fileID"];
        fileID = [TAPUtil nullToEmptyString:fileID];
        if (![fileID isEqualToString:@""]) {
            NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:fileID];
            if (![filePath isEqualToString:@""] && filePath != nil) {
                //File exist
                [alertController addAction:saveToGalleryAction];
            }
        }
    }
    
    if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID] && !message.isSending) {
        //Show delete message for our bubble (my bubble) only
        [alertController addAction:deleteMessageAction];
    }
    
    [alertController addAction:cancelAction];
    
    if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) {
        self.isKeyboardWasShowed = YES;
    }
    else {
        self.isKeyboardWasShowed = NO;
    }
    
    [UIView animateWithDuration:0.2f animations:^{
        [self.messageTextView resignFirstResponder];
        [self.secondaryTextField resignFirstResponder];
    } completion:^(BOOL finished) {
        [self presentViewController:alertController animated:YES completion:^{
            //after animation
        }];
    }];
}

- (void)setReplyMessageWithMessage:(TAPMessageModel *)message {
    
    TAPChatManagerQuoteActionType type = [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID];
    if (type == TAPChatManagerQuoteActionTypeForward) {
        if ([message.forwardFrom.localID isEqualToString:@""] && [message.forwardFrom.fullname isEqualToString:@""]) {
            
            if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                self.replyMessageNameLabel.text = NSLocalizedString(@"You", @"");
            }
            else {
                self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.user.fullname];
            }
        }
        else {
            //check id message sender is equal to active user id, if yes change the title to "You"
            if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                self.replyMessageNameLabel.text = NSLocalizedString(@"You", @"");
            }
            else {
                self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.forwardFrom.fullname];
            }
        }
        
        self.replyMessageMessageLabel.text = [TAPUtil nullToEmptyString:message.body];
    }
    else {
        
        //check id message sender is equal to active user id, if yes change the title to "You"
        if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
            self.replyMessageNameLabel.text = NSLocalizedString(@"You", @"");
        }
        else {
            self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.user.fullname];
        }
        
        self.replyMessageMessageLabel.text = [TAPUtil nullToEmptyString:message.body];
    }
}

- (void)setQuoteWithQuote:(TAPQuoteModel *)quote userID:(NSString *)userID {
    
    //check id message sender is equal to active user id, if yes change the title to "You"
    if ([userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
        self.quoteTitleLabel.text = NSLocalizedString(@"You", @"");
    }
    else {
        self.quoteTitleLabel.text = quote.title;
    }
    
    self.quoteSubtitleLabel.text = quote.content;
    
    if ([quote.fileType isEqualToString:[NSString stringWithFormat:@"%ld", TAPChatMessageTypeFile]] || [quote.fileType isEqualToString:@"file"]) {
        //TYPE FILE
        self.quoteFileView.alpha = 1.0f;
        self.quoteImageView.alpha = 0.0f;
    }
    else {
        if (quote.imageURL != nil && ![quote.imageURL isEqualToString:@""]) {
            [self.quoteImageView setImageWithURLString:quote.imageURL];
        }
        else if (quote.fileID != nil && ![quote.fileID isEqualToString:@""]) {
            [self.quoteImageView setImageWithURLString:quote.fileID];
        }
        self.quoteFileView.alpha = 0.0f;
        self.quoteImageView.alpha = 1.0f;
    }
}


#pragma mark Input Accessory View
//Implement Input Accessory View
- (UIView *)inputAccessoryView {
    /*
     //Change to this if method if there are bug showing compose keyboard view, but this method causing another problem which compose view sometimes not appear because wrong detection of active view controller
     if ((self.isViewWillAppeared || self.isSwipeGestureEnded)  && ([[[[[TapUI sharedInstance] getCurrentTapTalkActiveViewController] class] description] isEqualToString:[[TapUIChatViewController class] description]] || [[[[[TapUI sharedInstance] getCurrentTapTalkActiveViewController] class] description] isEqualToString:[[TAPMediaDetailViewController class] description]])) {
     
    }
     */
    
    if ((self.isViewWillAppeared || self.isSwipeGestureEnded)) {
        if (self.isShowAccessoryView) {
            return self.inputMessageAccessoryView;
        }
        else {
            return nil;
        }
    }
    else {
        return nil;
    }
}

- (void)showInputAccessoryExtensionView:(BOOL)show {
    if (show) {
        _currentInputAccessoryExtensionHeight = kInputMessageAccessoryExtensionViewDefaultHeight;
        
        if (self.isKeyboardShowed) {
            _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + self.initialKeyboardHeight;
        }
        else {
            _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight;
        }
        
        if (self.isKeyboardShowedForFirstTime) {
            [UIView animateWithDuration:0.2f animations:^{
                self.inputAccessoryExtensionHeightConstraint.constant = self.currentInputAccessoryExtensionHeight;
                [self.inputAccessoryView layoutIfNeeded];
                [[[self.inputAccessoryView superview] superview] layoutIfNeeded];
            }];
        }
        else {
            self.inputAccessoryExtensionHeightConstraint.constant = self.currentInputAccessoryExtensionHeight;
        }
    }
    else {
        _currentInputAccessoryExtensionHeight = 0.0f;
        
        if (self.isKeyboardShowed) {
            _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + self.initialKeyboardHeight;
        }
        else {
            _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight;
        }
        
        if (self.isKeyboardShowedForFirstTime) {
            [UIView animateWithDuration:0.2f animations:^{
                self.inputAccessoryExtensionHeightConstraint.constant = 0.0f;
                [self.inputAccessoryView layoutIfNeeded];
                [[[self.inputAccessoryView superview] superview] layoutIfNeeded];
            }];
        }
        else {
            self.inputAccessoryExtensionHeightConstraint.constant = 0.0f;
        }
        
        if (self.isInputAccessoryExtensionShowedFirstTimeOpen) {
            _initialKeyboardHeight = 0.0f;
            _isInputAccessoryExtensionShowedFirstTimeOpen = NO;
        }
    }
}

- (void)setInputAccessoryExtensionType:(InputAccessoryExtensionType)inputAccessoryExtensionType {
    _inputAccessoryExtensionType = inputAccessoryExtensionType;
    if (inputAccessoryExtensionType == inputAccessoryExtensionTypeQuote) {
        self.quoteView.alpha = 1.0f;
        self.replyMessageView.alpha = 0.0f;
    }
    else if (inputAccessoryExtensionType == inputAccessoryExtensionTypeReplyMessage) {
        self.quoteView.alpha = 0.0f;
        self.replyMessageView.alpha = 1.0f;
    }
}

- (IBAction)inputAccessoryExtensionCloseButtonDidTapped:(id)sender {
    [self showInputAccessoryExtensionView:NO];
    [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID];
}

- (void)showInputAccessoryView {
    _isShowAccessoryView = YES;
    [self reloadInputViews];
    [self becomeFirstResponder];
}

#pragma mark Chat Data Flow
- (void)firstLoadData {
    TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = roomData.roomID;
    
    NSDate *date = [NSDate date];
    NSTimeInterval createdDate = [date timeIntervalSince1970] * 1000.0f; //Timestamp in miliseconds
    
    _isFirstLoadData = YES;
    
    [self fetchUnreadMessagesDataWithSuccess:^(NSArray *unreadMessages) {
        if ([unreadMessages count] != 0) {
            //Obtain earliest unread message index
            TAPMessageModel *earliestUnreadMessage = [unreadMessages firstObject];
            _unreadLocalID = earliestUnreadMessage.localID;
            _numberOfUnreadMessages = [unreadMessages count];
        }
        
        [TAPDataManager getMessageWithRoomID:roomID lastMessageTimeStamp:[NSNumber numberWithDouble:createdDate] limitData:TAP_NUMBER_OF_ITEMS_CHAT success:^(NSArray<TAPMessageModel *> *obtainedMessageArray) {
            
            //DV Note - check method checkAndShowRoomViewState too if wants to update code below
            //Check if room is deleted or kicked
            TAPMessageModel *lastMessage = [obtainedMessageArray firstObject];
            if (lastMessage.room.type == RoomTypePersonal && lastMessage.room.isDeleted) {
                [self.view endEditing:YES];
                [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
            }
            else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                //Check if system message with action remove participant and target user is current user
                //show deleted chat room view
                [self.view endEditing:YES];
                [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO];
            }
            else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/delete"]) {
                [self.view endEditing:YES];
                if (lastMessage.room.type == RoomTypePersonal) {
                    [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
                }
                else if (lastMessage.room.type == RoomTypeGroup) {
                    [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES];
                }
            }
            else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/leave"] && [lastMessage.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                [self.view endEditing:YES];
                [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
            }
            //END DV Note
            
            if ([obtainedMessageArray count] == 0) {
                //No chat history, first time chat
                [self checkEmptyState];
                
                //Reload View
                [self.tableView reloadData];
                
                //Obtain Current Timestamp
                NSNumber *currentMaxCreated = [NSNumber numberWithLong:createdDate];
                
                //Call API Before With Current Timestamp And Update UI
                [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:roomID maxCreated:currentMaxCreated];
            }
            else {
                //Has existing chat
                [self updateMessageDataAndUIWithMessages:obtainedMessageArray checkFirstUnreadMessage:NO toTop:NO updateUserDetail:NO withCompletionHandler:^{
                    TAPMessageModel *earliestMessage = [obtainedMessageArray objectAtIndex:[obtainedMessageArray count] - 1];
                    NSNumber *minCreated = earliestMessage.created;  
                    _minCreatedMessage = minCreated;
                    
                    TAPMessageModel *latestMessage = [obtainedMessageArray objectAtIndex:0];
                    NSNumber *maxCreated = latestMessage.created;
                    
                    NSNumber *lastUpdated = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID];
                    if ([lastUpdated longLongValue] == 0 || lastUpdated == nil) {
                        //First time call, set minCreated to lastUpdated preference
                        [TAPDataManager setMessageLastUpdatedWithRoomID:roomID lastUpdated:minCreated];
                    }
                    
                    //Call API Get After Message
                    //Obtain Last Updated Value
                    NSNumber *lastUpdatedFromPreference = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID];
                    [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:minCreated lastUpdated:lastUpdatedFromPreference needToSaveLastUpdatedTimestamp:YES success:^(NSArray *messageArray) {
                        
                        //Delete physical files when isDeleted = 1 (message is deleted)
                        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
                        dispatch_async(queue, ^{
                            for (TAPMessageModel *message in messageArray) {
                                if (message.isDeleted) {
                                    [TAPDataManager deletePhysicalFilesInBackgroundWithMessage:message success:^{
                                        
                                    } failure:^(NSError *error) {
                                        
                                    }];
                                }
                            }
                        });
                        
                        
                        //Update View
                        [self updateMessageDataAndUIWithMessages:messageArray checkFirstUnreadMessage:YES toTop:YES updateUserDetail:YES withCompletionHandler:^{
                            
                            //Update leftover message status to delivered
                            if ([messageArray count] != 0) {
                                [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray];
                            }
                            
                            //Call API Before Message if count < 50
                            if ([self.messageArray count] < TAP_NUMBER_OF_ITEMS_CHAT) {
                                [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:roomID maxCreated:minCreated];
                            }
                            else {
                                [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
                                _isFirstLoadData = NO;
                            }
                            
                            [self processAllPreviousMessageAsRead];
                            //check if last message is deleted room
                            [self checkAndShowRoomViewState];
                        }];
                    } failure:^(NSError *error) {
                        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
                        
                        _isFirstLoadData = NO;
                        
                        //check if last message is deleted room
                        TAPMessageModel *lastMessage = [self.messageArray firstObject];
                        if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                            //Check if system message with action remove participant and target user is current user
                            //show deleted chat room view
                            [self.view endEditing:YES];
                            [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO];
                        }
#ifdef DEBUG
                        //Note - this alert only shown at debug
                        NSString *errorMessage = [error.userInfo objectForKey:@"message"];
                        errorMessage = [TAPUtil nullToEmptyString:errorMessage];
                        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Failed", @"") message:errorMessage preferredStyle:UIAlertControllerStyleAlert];
                        
                        UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                        }];
                        
                        [alertController addAction:okAction];
                        [self presentViewController:alertController animated:YES completion:nil];
#endif
                        [self processAllPreviousMessageAsRead];
                    }];
                }];
            }
        } failure:^(NSError *error) {
            [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
            
            [self processAllPreviousMessageAsRead];
        }];
        
    } failure:^(NSError *error) {
        
    }];
}

- (void)addIncomingMessageToArrayAndDictionaryWithMessage:(TAPMessageModel *)message atIndex:(NSInteger)index {
    
    //Add message to message pointer dictionary
    [self.messageDictionary setObject:message forKey:message.localID];
    
    //Add message to data array
    [self.messageArray insertObject:message atIndex:index];
}

- (void)removeMessageFromArrayAndDictionaryWithLocalID:(NSString *)localID {
    TAPMessageModel *currentRemovedMessage = [self.messageDictionary objectForKey:localID];
    [self.messageDictionary removeObjectForKey:localID];
    [self.messageArray removeObject:currentRemovedMessage];
}

- (void)handleMessageFromSocket:(TAPMessageModel *)message isUpdatedMessage:(BOOL)isUpdated {
    dispatch_async(dispatch_get_main_queue(), ^{
        //Check if message exist in Message Pointer Dictionary
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID];
        if(currentMessage != nil) {
            //Message exist in dictionary
            
            //Update message into array and dictionary
            //Need to take message before data updated to get current sending state
            
            TAPUserModel *currentUser = [TAPDataManager getActiveUser];
            
            BOOL isSendingAnimation = NO;
            BOOL setAsDelivered = NO;
            BOOL setAsRead = NO;
            BOOL setAsDeleted = NO;
            
            
            if ([currentMessage.user.userID isEqualToString:currentUser.userID]) {
                //My Message
                if (currentMessage.isSending) {
                    //Message was sending
                    isSendingAnimation = YES;
                    NSInteger indexInArray = [self.messageArray indexOfObject:currentMessage];
                }
                
                if(!currentMessage.isDelivered && message.isDelivered && !currentMessage.isRead && !message.isRead) {
                    setAsDelivered = YES;
                    setAsDeleted = NO;
                }
                
                if(!currentMessage.isRead && message.isRead) {
                    setAsDelivered = NO;
                    setAsRead = YES;
                }
                
                //check if current message from socket is deleted, not from message in local array or dictionary
                if (message.isDeleted) {
                    setAsDeleted = YES;
                }
            }
            else {
                //Their Message
                //check if current message from socket is deleted, not from message in local array or dictionary
                if (message.isDeleted) {
                    setAsDeleted = YES;
                }
            }
            
            //Update message data
            [self updateMessageModelValueWithMessage:message];
            
            //Check need to update profile data or not
            [self checkUpdatedUserProfileWithMessage:message];
            
            //Update view
            NSInteger indexInArray = [self.messageArray indexOfObject:currentMessage];
            NSIndexPath *messageIndexPath = [NSIndexPath indexPathForRow:indexInArray inSection:0];
            
            if (setAsDeleted) {
                //Delete physical file if exist
                if (currentMessage.type == TAPChatMessageTypeImage || currentMessage.type == TAPChatMessageTypeVideo || currentMessage.type == TAPChatMessageTypeFile) {
                    [TAPDataManager deletePhysicalFilesWithMessage:currentMessage success:^{
                        
                    } failure:^(NSError *error) {
                        
                    }];
                }
                
                //Update cell to deleted message
                [self.tableView beginUpdates];
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:messageIndexPath, nil] withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView endUpdates];
            }
            else {
                if (currentMessage.type == TAPChatMessageTypeText) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        
                        if (isSendingAnimation) {
                            [cell receiveSentEvent];
                        }
                        else if (setAsDelivered) {
                            [cell receiveDeliveredEvent];
                        }
                        else if (setAsRead) {
                            [cell receiveReadEvent];
                        }
                        else {
                            [cell setMessage:message];
                            
                            //        //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related
                            //        [self.tableView reloadData];
                        }
                    }
                }
                else if (currentMessage.type == TAPChatMessageTypeImage) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        TAPMyImageBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        
                        if (isSendingAnimation) {
                            [cell receiveSentEvent];
                        }
                        else if (setAsDelivered) {
                            [cell receiveDeliveredEvent];
                        }
                        else if (setAsRead) {
                            [cell receiveReadEvent];
                        }
                        else {
                            [cell setMessage:message];
                            
                            //        //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related
                            //        [self.tableView reloadData];
                        }
                    }
                }
                else if (currentMessage.type == TAPChatMessageTypeVideo) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        //My Chat
                        TAPMyVideoBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        cell.message = currentMessage;
                        
                        if (isSendingAnimation) {
                            [cell receiveSentEvent];
                        }
                        else if (setAsDelivered) {
                            [cell receiveDeliveredEvent];
                        }
                        else if (setAsRead) {
                            [cell receiveReadEvent];
                        }
                        else {
                            [cell setMessage:message];
                            
                            //        //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related
                            //        [self.tableView reloadData];
                        }
                    }
                }
                else if (currentMessage.type == TAPChatMessageTypeFile) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        TAPMyFileBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        
                        if (isSendingAnimation) {
                            [cell receiveSentEvent];
                        }
                        else if (setAsDelivered) {
                            [cell receiveDeliveredEvent];
                        }
                        else if (setAsRead) {
                            [cell receiveReadEvent];
                        }
                        else {
                            [cell setMessage:message];
                            
                            //        //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related
                            //        [self.tableView reloadData];
                        }
                    }
                }
                else if (currentMessage.type == TAPChatMessageTypeProduct) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        TAPProductListBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        NSArray *productListArray = [currentMessage.data objectForKey:@"items"];
                        [cell setProductListBubbleCellWithData:productListArray];
                        if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                            [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeSingleOption];
                        }
                        else {
                            [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeTwoOption];
                        }
                    }
                }
                else if (currentMessage.type == TAPChatMessageTypeLocation) {
                    if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
                        TAPMyLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        
                        if (isSendingAnimation) {
                            [cell receiveSentEvent];
                        }
                        else if (setAsDelivered) {
                            [cell receiveDeliveredEvent];
                        }
                        else if (setAsRead) {
                            [cell receiveReadEvent];
                        }
                        else {
                            [cell setMessage:message];
                            
                            //        //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related
                            //        [self.tableView reloadData];
                        }
                    }
                }
                else {
                    //check if custom bubble available
                    NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type];
                    
                    if([cellDataDictionary count] > 0 && cellDataDictionary != nil) {
                        //if custom bubble from client available
                        
                        TAPBaseGeneralBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath];
                        [cell setMessage:message];
                        [self.tableView beginUpdates];
                        [self.tableView endUpdates];
                    }
                }
            }
        }
        else {
            //Message not exist in dictionary
            if (!isUpdated) {
                //Only run when message is new message
                if(self.tableView.contentOffset.y > kShowChatAnchorOffset) {
                    //Bottom table view not seen, put message to holder array and insert the message when user scroll to bottom
                    [self.scrolledPendingMessageArray insertObject:message atIndex:0];
                    
                    //Add message to messageDictionary first to lower load time (pending message will be inserted to messageArray at scrollViewDidScroll and chatAnchorButtonDidTapped)
                    [self.messageDictionary setObject:message forKey:message.localID];
                    
                    [self addMessageToAnchorUnreadArray:message];
                }
                else {
                    //RN Note - If crash happen on opening room see updateMessageDataAndUIWithMessages method
                    //Bottom table view visible, insert message normally
                    [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:0];
                    NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
                    [self.tableView beginUpdates];
                    [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationTop];
                    [self.tableView endUpdates];
                }
            }
        }
        [self checkEmptyState];
    });
}

- (void)destroySequence {
    //Save to draft
    [self saveMessageDraft];
    
    //Update badge count
    [[TAPNotificationManager sharedManager] updateApplicationBadgeCount];
    
    [[TAPChatManager sharedManager] stopTyping];
    
    [[TAPChatManager sharedManager] closeActiveRoom];
    
    //Remove ChatManager Delegate
    [[TAPChatManager sharedManager] removeDelegate:self];
}

- (void)addMessageToAnchorUnreadArray:(TAPMessageModel *)message {
    if (![self.anchorUnreadMessageArray containsObject:message]) {
        [self.anchorUnreadMessageArray addObject:message];
        [self checkAnchorUnreadLabel];
    }
}

- (void)removeMessageFromAnchorUnreadArray:(TAPMessageModel *)message {
    if ([self.anchorUnreadMessageArray containsObject:message]) {
        [self.anchorUnreadMessageArray removeObject:message];
        [self checkAnchorUnreadLabel];
    }
}

- (void)retrieveExistingMessages {
    //Prevent retreive before message if already last page
    if (self.isLastPage) {
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
        return;
    }
    
    TAPMessageModel *lastMessage = [self.messageArray lastObject];
    
    if (self.apiBeforeLastCreated == [lastMessage.created longLongValue]) {
        return;
    }
    
    _apiBeforeLastCreated = [lastMessage.created longLongValue];
    
    [TAPDataManager getMessageWithRoomID:lastMessage.room.roomID lastMessageTimeStamp:lastMessage.created limitData:TAP_NUMBER_OF_ITEMS_CHAT success:^(NSArray<TAPMessageModel *> *obtainedMessageArray) {
        if ([obtainedMessageArray count] > 0) {
            [self updateMessageDataAndUIFromBeforeWithMessages:obtainedMessageArray withCompletionHandler:^{
                //if there's tapped reply message id, check and scroll to item
                if (![TAPUtil isEmptyString:self.tappedMessageLocalID]) {
                    //Add 0.5s delay to wait update table view UI  from previous update message
                    [TAPUtil performBlock:^{
                        [self scrollToMessageAndLoadDataWithLocalID:self.tappedMessageLocalID];
                    } afterDelay:0.5f];
                }
            }];
        }
        
        //Call API Before when message array less than limit (50)
        [TAPUtil performBlock:^{
            //Add 0.2s delay to wait update table view UI from previous update message
            if ([obtainedMessageArray count] < TAP_NUMBER_OF_ITEMS_CHAT && !self.isFirstLoadData) {
                [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:lastMessage.room.roomID maxCreated:lastMessage.created];
            }
        } afterDelay:0.2f];
    
    } failure:^(NSError *error) {
        
    }];
}

- (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated {
    //Call API Get Before Message
    if ([self.loadedMaxCreated longLongValue] != [maxCreated longLongValue]) {
        _loadedMaxCreated = maxCreated;
        [self showLoadMessageCellLoading:YES];
        [TAPDataManager callAPIGetMessageBeforeWithRoomID:roomID maxCreated:maxCreated numberOfItems:[NSNumber numberWithInteger:TAP_NUMBER_OF_ITEMS_API_MESSAGE_BEFORE] success:^(NSArray *messageArray, BOOL hasMore) {
            if ([messageArray count] != 0) {
                
                _isLastPage = !hasMore;
                
                [self showLoadMessageCellLoading:NO];
                
                //Update View
                [self updateMessageDataAndUIFromBeforeWithMessages:messageArray withCompletionHandler:^{
                    //if there's tapped reply message id, check and scroll to item
                    if (![TAPUtil isEmptyString:self.tappedMessageLocalID]) {
                        [self scrollToMessageAndLoadDataWithLocalID:self.tappedMessageLocalID];
                    }
                }];
            }
            else if ([messageArray count] == 0 && !hasMore) {
                [self showLoadMessageCellLoading:NO];
            }
            _isFirstLoadData = NO;
        } failure:^(NSError *error) {
            [self showLoadMessageCellLoading:NO];
            _isFirstLoadData = NO;
#ifdef DEBUG
            //Note - this alert only shown at debug
            NSString *errorMessage = [error.userInfo objectForKey:@"message"];
            errorMessage = [TAPUtil nullToEmptyString:errorMessage];
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Failed", @"") message:errorMessage preferredStyle:UIAlertControllerStyleAlert];
            
            UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            }];
            
            [alertController addAction:okAction];
            [self presentViewController:alertController animated:YES completion:nil];
#endif
        }];
    }
}

- (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray checkFirstUnreadMessage:(BOOL)checkFirstUnreadMessage toTop:(BOOL)toTop updateUserDetail:(BOOL)updateUserDetail withCompletionHandler:(void(^)())completionHandler {
    //RN Note - If crash happen on opening room, this async might be the cause
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSInteger earliestUnreadMessageIndex = -1;
        long minCreatedUnreadMessage;
        
        for (NSInteger counter = 0; counter < [messageArray count]; counter++) {
            
            TAPMessageModel *message = [messageArray objectAtIndex:counter];
            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID];
            
            if (currentMessage != nil) {
                //Message exist in dictionary
                [self updateMessageModelValueWithMessage:message];
            }
            else {
                //Message not exist in dictionary
                NSInteger index = 0;
                
                if (!toTop) {
                    index = [self.messageArray count];
                }

                [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:index];
                if (checkFirstUnreadMessage && message.isRead == 0) {
                    //For checking unread message from API After
                    
                    if (self.unreadLocalID == nil || [self.unreadLocalID isEqualToString:@""]) {
                        //save first unread message localID if unreadLocalID is nil
                        self.unreadLocalID = message.localID;
                    }
                    
                    _numberOfUnreadMessages = self.numberOfUnreadMessages + 1;
                    
                    //Obtain smallest created of unread message
                    long currentMessageCreated = [message.created longValue];
                    if (counter == 0) {
                        minCreatedUnreadMessage = currentMessageCreated;
                        earliestUnreadMessageIndex = 0;
                    }
                    else {
                        if (currentMessageCreated < minCreatedUnreadMessage) {
                            //Set the smallest created timestamp
                            minCreatedUnreadMessage = currentMessageCreated;
                            earliestUnreadMessageIndex = counter;
                        }
                    }
                }
            }
            
            if (updateUserDetail) {
                //Check need to update profile data or not
                [self checkUpdatedUserProfileWithMessage:message];
            }
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            //Check to show unread message identifier
            if (!self.isShowingUnreadMessageIdentifier) {
                if (checkFirstUnreadMessage) {
                    //From API After
                    if (earliestUnreadMessageIndex != -1) {
                        TAPMessageModel *smallestCreatedUnreadMessage = [messageArray objectAtIndex:earliestUnreadMessageIndex];
                        NSString *obtainedLocalID = smallestCreatedUnreadMessage.localID;
                        TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:obtainedLocalID];
                        NSInteger unreadMessageIndex = [self.messageArray indexOfObject:obtainedMessage];
                        
                        if(NSNotFound != unreadMessageIndex) {
                            //Only run when index in found in message array
                            //Construct unread message identifier and add to view (messageIndex + 1 to add above earliest message)
                            NSInteger createdInteger = [obtainedMessage.created integerValue];
                            createdInteger = createdInteger - 1; //min 1 to set created earlier than the first obtained unread
                            TAPMessageModel *generatedMessage = [[TAPChatManager sharedManager] generateUnreadMessageIdentifierWithRoom:obtainedMessage.room created:[NSNumber numberWithInteger:createdInteger] indexPosition:unreadMessageIndex + 1];
                            [self addIncomingMessageToArrayAndDictionaryWithMessage:generatedMessage atIndex:unreadMessageIndex + 1];
                            _isShowingUnreadMessageIdentifier = YES;
                        }
                    }
                }
                else {
                    //From Database
                    NSString *unreadMessageLocalID = self.unreadLocalID;
                    TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:unreadMessageLocalID];
                    NSInteger messageIndex = [self.messageArray indexOfObject:obtainedMessage];
                    
                    if(NSNotFound != messageIndex) {
                        //Only run when index in found in message array
                        //Construct unread message identifier and add to view (messageIndex + 1 to add above earliest message)
                        NSInteger createdInteger = [obtainedMessage.created integerValue];
                        createdInteger = createdInteger - 1; //min 1 to set created earlier than the first obtained unread
                        TAPMessageModel *generatedMessage = [[TAPChatManager sharedManager] generateUnreadMessageIdentifierWithRoom:obtainedMessage.room created:[NSNumber numberWithInteger:createdInteger] indexPosition:messageIndex + 1];
                        [self addIncomingMessageToArrayAndDictionaryWithMessage:generatedMessage atIndex:messageIndex + 1];
                        _isShowingUnreadMessageIdentifier = YES;
                    }
                }
            }
    
            [self sortAndFilterMessageArray];
            
            [self.tableView reloadData];
            
            //Check to show top unread button
            [self checkAndShowUnreadButton];
            
            if (toTop) {
                //RN To Do - Scroll to "Unread Message" marker after implemented
                [self.tableView scrollsToTop];
            }
            
            [self checkEmptyState];
            
            completionHandler();
        });
    });
}

- (void)updateMessageDataAndUIFromBeforeWithMessages:(NSArray *)messageArray
                               withCompletionHandler:(void(^)())completionHandler {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSMutableArray *currentAddedMessageArray = [NSMutableArray array];
        NSMutableDictionary *currentAddedMessageDictionary = [NSMutableDictionary dictionary];

        NSInteger index = 0;
        index = [self.messageArray count];
        
        for (NSInteger counter = 0; counter < [messageArray count]; counter++) {
            TAPMessageModel *message = [messageArray objectAtIndex:counter];
            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID];
            if (currentMessage != nil) {
                //Message exist in dictionary
                [self updateMessageModelValueWithMessage:message];
            }
            else {
                //Message not exist in dictionary
                [currentAddedMessageArray addObject:[NSString stringWithFormat:@"%ld", index]];
                [currentAddedMessageDictionary setObject:message forKey:[NSString stringWithFormat:@"%ld", index]];
                index++;
            }
        }
            
            dispatch_async(dispatch_get_main_queue(), ^{
                for (NSString *key in currentAddedMessageArray) {
                    TAPMessageModel *currentMessage = [currentAddedMessageDictionary objectForKey:key];
                    [self addIncomingMessageToArrayAndDictionaryWithMessage:currentMessage atIndex:[key integerValue]];
                }
                
                [self.tableView reloadData]; //This logic might affect performance load when load before API, see and fix below implementation for better performance
                
//                //RN Notes - RN Debt - Uncommand this method to insert the message to table view without reload data, might cause crash when open room and load api before, and directly send message
//                NSMutableArray *indexPathArray = [NSMutableArray array];
//                NSInteger currentCount = [self.messageArray count] - [currentAddedMessageArray count];
//                for (int count = currentCount; count < [self.messageArray count]; count++) {
//                    [indexPathArray addObject:[NSIndexPath indexPathForRow:count inSection:0]];
//                }
//
//                if([indexPathArray count] > 0) {
//                    [self.tableView beginUpdates];
//                    [self.tableView insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationAutomatic];
//                    [self.tableView endUpdates];
//                    [self.tableView scrollsToTop];
//                }
                
                completionHandler();
            });
    });
}

- (void)sortAndFilterMessageArray {
    NSMutableArray *currentMessageArray = [NSMutableArray arrayWithArray:self.messageArray];
    
    NSMutableArray *sortedArray;
    
    sortedArray = [currentMessageArray sortedArrayUsingComparator:^NSComparisonResult(id message1, id message2) {
        TAPMessageModel *messageModel1 = (TAPMessageModel *)message1;
        TAPMessageModel *messageModel2 = (TAPMessageModel *)message2;
        
        NSNumber *message1CreatedDate = messageModel1.created;
        NSNumber *message2CreatedDate = messageModel2.created;
        
        return [message2CreatedDate compare:message1CreatedDate];
    }];
    
    _messageArray = [NSMutableArray arrayWithArray:sortedArray];
}

- (void)callAPIAfterAndUpdateUIAndScrollToTop:(BOOL)scrollToTop {
    TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = roomData.roomID;
    
    [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
    
    //Obtain Last Updated Value
    NSNumber *lastUpdatedFromPreference = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID];
    [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:self.minCreatedMessage lastUpdated:lastUpdatedFromPreference needToSaveLastUpdatedTimestamp:YES success:^(NSArray *messageArray) {
        
        //Delete physical files when isDeleted = 1 (message is deleted)
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            for (TAPMessageModel *message in messageArray) {
                if (message.isDeleted) {
                    [TAPDataManager deletePhysicalFilesInBackgroundWithMessage:message success:^{
                        
                    } failure:^(NSError *error) {
                        
                    }];
                }
            }
        });
        
        //Update View
        [self updateMessageDataAndUIWithMessages:messageArray checkFirstUnreadMessage:YES toTop:scrollToTop updateUserDetail:YES withCompletionHandler:^{
            //Update leftover message status to delivered
            if ([messageArray count] != 0) {
                [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray];
            }
        }];
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
        //check if last message is deleted room
        [self checkAndShowRoomViewState];
        
    } failure:^(NSError *error) {
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
        //check if last message is deleted room
        TAPMessageModel *lastMessage = [self.messageArray firstObject];
        if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
            //Check if system message with action remove participant and target user is current user
            //show deleted chat room view
            [self.view endEditing:YES];
            [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO];
        }
    }];
}

- (void)updateMessageModelValueWithMessage:(TAPMessageModel *)message {
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID];
    currentMessage.messageID = message.messageID;
    currentMessage.localID = message.localID;
    currentMessage.type = message.type;
    currentMessage.body = message.body;
    currentMessage.room = message.room;
    currentMessage.recipientID = message.recipientID;
    currentMessage.created = message.created;
    currentMessage.user = message.user;
    currentMessage.isHidden = message.isHidden;
    currentMessage.isDeleted = message.isDeleted;
    currentMessage.isSending = message.isSending;
    currentMessage.isFailedSend = message.isFailedSend;
    currentMessage.data = message.data;
    
    if(!currentMessage.isDelivered) {
        //Update only when ui data is not delivered yet
        currentMessage.isDelivered = message.isDelivered;
    }
    
    if(!currentMessage.isRead) {
        //Update only when ui data is not read yet
        currentMessage.isRead = message.isRead;
    }
    
    if(!currentMessage.isDeleted) {
        //Update only when ui data is not deleted yet
        currentMessage.isDeleted = message.isDeleted;
    }
}

- (void)saveMessageDraft {
    //Save message draft to chat manager if exist
    NSString *messageString = self.messageTextView.textView.text;
    messageString = [messageString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = room.roomID;
    roomID = [TAPUtil nullToEmptyString:roomID];
    [[TAPChatManager sharedManager] saveMessageToDraftWithMessage:messageString roomID:roomID];
}

- (void)processMessageAsRead:(TAPMessageModel *)message forceMarkAsRead:(BOOL)force {
    BOOL isRead = message.isRead;
    
    if(!self.isViewDidAppeared && !force) {
        //Do not process mark as read if from first view layout, visible message will be processed at processVisibleMessageAsRead
        return;
    }
    
    if(isRead) {
        //Do not process if message has been read
        return;
    }
    
    //Remove local notification and send read status to server
    message.isRead = YES;
    
    //Call Message Status Manager mark as read call API
    [[TAPMessageStatusManager sharedManager] markMessageAsReadWithMessage:message];
}

- (void)processVisibleMessageAsRead {
    NSArray *visibleCellIndexPathArray = [self.tableView indexPathsForVisibleRows];
    
    for(NSIndexPath *indexPath in visibleCellIndexPathArray) {
        if (indexPath.row >= [self.messageArray count]) {
            continue;
        }
        
        TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row];
        
        if (![currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
            //Their chat
            [self processMessageAsRead:currentMessage forceMarkAsRead:NO];
        }
    }
}

- (void)processAllPreviousMessageAsRead {
    if ([self.delegate respondsToSelector:@selector(chatViewControllerShouldClearUnreadBubbleForRoomID:)]) {
        [self.delegate chatViewControllerShouldClearUnreadBubbleForRoomID:self.currentRoom.roomID];
    }
    
    [TAPDataManager getDatabaseUnreadMessagesInRoomWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID
                                                 activeUserID:[TAPChatManager sharedManager].activeUser.userID
                                                      success:^(NSArray *unreadMessages) {
                                                          for(TAPMessageModel *currentMessageModel in unreadMessages) {
                                                              //Find current message model object in dictionary
                                                              TAPMessageModel *messageModel = [self.messageDictionary objectForKey:currentMessageModel.localID];
                                                              
                                                              if(messageModel != nil) {
                                                                  //Use object in dictionary if exist
                                                                  //Mark as read
                                                                  [self processMessageAsRead:messageModel forceMarkAsRead:YES];
                                                              }
                                                              else {
                                                                  //Message is not loaded yet in room, use current message model
                                                                  //Mark as read
                                                                  [self processMessageAsRead:currentMessageModel forceMarkAsRead:YES];
                                                              }
                                                          }
                                                      } failure:^(NSError *error) {
                                                          
                                                      }];
}

#pragma mark Keyboard
- (void)keyboardWillShowWithHeight:(CGFloat)keyboardHeight {
    if(!self.isKeyboardShowedForFirstTime) {
        _isKeyboardShowedForFirstTime = YES;
    }
    
    if (self.isKeyboardOptionTapped && self.isKeyboardShowed) {
        _keyboardHeight = keyboardHeight;
        CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight;
        
        [UIView animateWithDuration:0.2f animations:^{
            self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
            self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
            
            self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
            self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
        } completion:^(BOOL finished) {
            //Do something after animation completed.
        }];
        
        return;
    }
    
    CGFloat accessoryViewAndSafeAreaHeight = self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight;
    
    //set initial keyboard height to prevent wrong keyboard height usage
    if (self.initialKeyboardHeight == 0.0f && keyboardHeight !=  accessoryViewAndSafeAreaHeight && keyboardHeight != kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding && keyboardHeight != kInputMessageAccessoryViewHeight) {
        _initialKeyboardHeight = keyboardHeight - self.currentInputAccessoryExtensionHeight;
    }
    
    if (self.keyboardHeight == 0.0f) {
        //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight
        if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) {
            _lastKeyboardHeight = self.keyboardHeight;
            _keyboardHeight = keyboardHeight;
        }
    }
    CGFloat tempHeight = 0.0f;
    if (keyboardHeight > self.keyboardHeight) {
        //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight
        if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) {
            tempHeight = self.keyboardHeight;
            _lastKeyboardHeight = self.keyboardHeight;
            _keyboardHeight = keyboardHeight;
        }
    }
    
    //handle change keyboard height if keyboard is change to emoji
    if (keyboardHeight > self.initialKeyboardHeight && keyboardHeight != accessoryViewAndSafeAreaHeight) {
        _lastKeyboardHeight = self.keyboardHeight;
        _keyboardHeight = keyboardHeight;
    }
    
    //set keyboard height to initial height
    if (keyboardHeight == self.initialKeyboardHeight && self.isKeyboardShowed) {
        _lastKeyboardHeight = self.keyboardHeight;
        _keyboardHeight = self.initialKeyboardHeight;
    }
    
    if (self.isKeyboardShowed) {
        [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight];
    }
    
    //reject if scrollView is being dragged
    if (self.isScrollViewDragged) {
        return;
    }
    
    CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight;
    
    CGFloat lastTableViewYContentInset = self.tableView.contentInset.top;
    
    self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
    self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
    
    [UIView animateWithDuration:0.2f animations:^{
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        
        CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight;
        if (messageViewHeightDifference < 0) {
            messageViewHeightDifference = 0.0f;
        }
        
        CGFloat newYContentOffset = self.tableView.contentOffset.y - self.keyboardHeight + self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference;
        
        if (fabs(tableViewYContentInset - lastTableViewYContentInset) == kInputMessageAccessoryExtensionViewDefaultHeight) {
            newYContentOffset = self.tableView.contentOffset.y + lastTableViewYContentInset - tableViewYContentInset;
        }
        
        if(self.isKeyboardShowed) {
            if (self.keyboardHeight > self.lastKeyboardHeight) {
                newYContentOffset = self.tableView.contentOffset.y + (self.lastKeyboardHeight - self.keyboardHeight);
            }
            else {
                newYContentOffset = self.tableView.contentOffset.y;
            }
        }
        
        if(self.tableView.contentOffset.y == 0.0f) {
            newYContentOffset = 0.0f;
        }
        
        if (newYContentOffset < tableViewYContentInset) {
            newYContentOffset = -tableViewYContentInset;
        }
        
        [self.tableView setContentOffset:CGPointMake(0.0f, newYContentOffset)];
        [self.view layoutIfNeeded];
        
        if (!self.isKeyboardShowed) {
            [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight];
        }
    } completion:^(BOOL finished) {
        //Do something after animation completed.
        //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight
        if (tempHeight != 0.0f && tempHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) {
            _lastKeyboardHeight = self.keyboardHeight;
            _keyboardHeight = tempHeight;
        }
    }];
    
    if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight != kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding && keyboardHeight != kInputMessageAccessoryViewHeight) {
        _isKeyboardShowed = YES;
    }
}

- (void)keyboardWillHideWithHeight:(CGFloat)keyboardHeight {
    
    if (self.isKeyboardOptionTapped && self.isKeyboardShowed) {
        return;
    }
    
    //set default keyboard height including accessory view height
    _keyboardHeight = self.messageViewHeightConstraint.constant + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight;
    
    //reject if scrollView is being dragged
    if (self.isScrollViewDragged) {
        _isKeyboardShowed = NO;
        return;
    }
    
    CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight;
    if (messageViewHeightDifference < 0) {
        messageViewHeightDifference = 0.0f;
    }
    
    self.tableView.contentInset = UIEdgeInsetsMake(self.currentInputAccessoryExtensionHeight + messageViewHeightDifference, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right);
    self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(self.currentInputAccessoryExtensionHeight, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right);
    
    [UIView animateWithDuration:0.2f animations:^{
        if(self.isCustomKeyboardAvailable) {
            self.keyboardOptionButtonView.alpha = 1.0f;
            self.keyboardOptionButton.alpha = 1.0f;
            self.keyboardOptionButton.userInteractionEnabled = YES;
            self.messageViewLeftConstraint.constant = 4.0f;
            self.keyboardOptionViewRightConstraint.constant = 16.0f;
            [self.inputMessageAccessoryView layoutIfNeeded];
        }
        
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference;
        self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference;
        
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        //Do something after animation completed.
    }];
    
    _isKeyboardShowed = NO;
}

- (void)checkKeyboard {
    //WK Note - To check if the keyboard was showed before attachment button tapped.
    if (self.isKeyboardWasShowed) {
        if (self.keyboardState == keyboardStateDefault) {
            [self.messageTextView becameFirstResponder];
        }
        else {
            [self.secondaryTextField becomeFirstResponder];
        }
    }
}

- (void)setKeyboardStateDefault {
    _keyboardState = keyboardStateDefault;    
    self.keyboardOptionButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerBurgerMenuBackground];
    UIImage *hamburgerIconImage = [UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    self.keyboardOptionButtonImageView.image = [self.keyboardOptionButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerBurgerMenu]];
    [self.keyboardOptionButton setImage:hamburgerIconImage forState:UIControlStateNormal];
}

- (void)setKeyboardStateOption {
    _keyboardState = keyboardStateOptions;

    self.keyboardOptionButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerShowKeyboardBackground];
    UIImage *keyboardIconImage = [UIImage imageNamed:@"TAPIconKeyboard" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    self.keyboardOptionButtonImageView.image = [self.keyboardOptionButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerShowKeyboard]];
    [self.keyboardOptionButton setImage:keyboardIconImage forState:UIControlStateNormal];
}

- (IBAction)keyboardOptionButtonDidTapped:(id)sender {
        
    _isKeyboardOptionTapped = YES;
    
    //Hide unread message indicator top view
    if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) {
        [TAPUtil performBlock:^{
            [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
        } afterDelay:1.0f];
    }
    
    if (self.keyboardState == keyboardStateDefault) {
        [self setKeyboardStateOption];
        
        [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight];
        
        self.secondaryTextField.inputView = self.keyboardViewController.inputView;
        if (IS_IPHONE_X_FAMILY) {
            if (self.isKeyboardShowed) {
                [UIView performWithoutAnimation:^{
                    [self.messageTextView resignFirstResponder];
                    [self.secondaryTextField becomeFirstResponder];
                }];
            }
            else {
                [self.secondaryTextField becomeFirstResponder];
            }
        }
        else {
            [self.secondaryTextField becomeFirstResponder];
        }
    }
    else {
        [self setKeyboardStateDefault];
        
        if (IS_IPHONE_X_FAMILY) {
            if (self.isKeyboardShowed) {
                [UIView performWithoutAnimation:^{
                    [self.secondaryTextField resignFirstResponder];
                    [self.messageTextView becameFirstResponder];
                }];
            }
            else {
                [self.messageTextView becameFirstResponder];
            }
        }
        else {
            [self.messageTextView becameFirstResponder];
        }
    }
    
    _isKeyboardOptionTapped = NO;
}

#pragma mark Others
- (void)setChatViewControllerType:(TapUIChatViewControllerType)chatViewControllerType {
    _chatViewControllerType = chatViewControllerType;
    
    if (self.chatViewControllerType == TapUIChatViewControllerTypePeek) {
        //Hide accessory view when peek 3D touch
        self.inputMessageAccessoryView.alpha = 0.0f;
        self.dummyNavigationBarView.alpha = 1.0f;
        self.dummyNavigationBarTitleLabel.alpha = 1.0f;
    }
    else {
        self.inputMessageAccessoryView.alpha = 1.0f;
        self.dummyNavigationBarView.alpha = 0.0f;
        self.dummyNavigationBarTitleLabel.alpha = 0.0f;
    }
}

- (void)applicationWillEnterForegroundNotification:(NSNotification *)notification {
    if ([self.messageArray count] > 0 && self.minCreatedMessage != nil && [self.minCreatedMessage integerValue] != 0) {
        [self callAPIAfterAndUpdateUIAndScrollToTop:YES];
    }
}

- (void)reachabilityStatusChange:(NSNotification *)notification {
    if ([AFNetworkReachabilityManager sharedManager].reachable) {
        if (self.isNeedRefreshOnNetworkDown) {
            //Update data from API when network down and reconnect
            [self callAPIAfterAndUpdateUIAndScrollToTop:NO];
            
            _isNeedRefreshOnNetworkDown = NO;
        }
    }
    else {
        _isNeedRefreshOnNetworkDown = YES;
    }
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)checkEmptyState {
    if ([self.messageArray count] == 0) {
        if (self.emptyView.alpha == 1.0f) {
            return;
        }
        
        //Show empty chat welcome message
        TAPUserModel *activeUser = [TAPDataManager getActiveUser];
        
        TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
        NSString *roomName = room.name;
        roomName = [TAPUtil nullToEmptyString:roomName];
        
        NSString *otherUserRoleCode = self.otherUser.userRole.code;
        otherUserRoleCode = [TAPUtil nullToEmptyString:otherUserRoleCode];
        
        UIFont *emptyTitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelSubtitle];
        UIColor *emptyTitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelSubtitle];
        UIFont *emptyTitleLabelBoldFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelSubtitleBold];
        UIColor *emptyTitleLabelBoldColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelSubtitleBold];
        UIFont *emptyDescriptionLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelBody];
        UIColor *emptyDescriptionLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelBody];
        
        self.emptyTitleLabel.font = emptyTitleLabelFont;
        self.emptyTitleLabel.textColor = emptyTitleLabelColor;
        
        self.emptyDescriptionLabel.font = emptyDescriptionLabelFont;
        self.emptyDescriptionLabel.textColor = emptyDescriptionLabelColor;

        NSString *emptyTitleString;
        NSString *emptyDescriptionString;

        if (self.currentRoom.type == RoomTypePersonal) {
            //Personal
            emptyTitleString = [NSString stringWithFormat:NSLocalizedString(@"Start a conversation with %@", @""), self.otherUser.fullname];
           emptyDescriptionString = [NSString stringWithFormat:NSLocalizedString(@"Say hi to %@ and start a conversation", @""), self.otherUser.fullname];
        }
        else {
            //Group or Channel
            emptyTitleString = [NSString stringWithFormat:NSLocalizedString(@"It seems to be quiet in %@", @""), self.currentRoom.name];
            emptyDescriptionString = NSLocalizedString(@"Say hi to the group and start the conversation", @"");
        }
        
        self.emptyTitleLabel.text = emptyTitleString;
        //set attributed string
        NSMutableDictionary *emptyTitleAttributesDictionary = [NSMutableDictionary dictionary];
        [emptyTitleAttributesDictionary setObject:emptyTitleLabelBoldFont forKey:NSFontAttributeName];
        [emptyTitleAttributesDictionary setObject:emptyTitleLabelBoldColor forKey:NSForegroundColorAttributeName];
        NSMutableAttributedString *emptyTitleAttributedString = [[NSMutableAttributedString alloc] initWithString:self.emptyTitleLabel.text];
        
        if(self.emptyTitleLabel.text != nil && ![self.emptyTitleLabel.text isEqualToString:@""]) {
            NSRange roomNameRange = [self.emptyTitleLabel.text rangeOfString:roomName];
            [emptyTitleAttributedString addAttributes:emptyTitleAttributesDictionary
                                                range:roomNameRange];
            self.emptyTitleLabel.attributedText = emptyTitleAttributedString;
        }
        
        self.emptyDescriptionLabel.text = emptyDescriptionString;
        
        
        NSString *senderImageURL = activeUser.imageURL.thumbnail;
        NSString *recipientImageURL = room.imageURL.thumbnail;
        if (senderImageURL == nil || [senderImageURL isEqualToString:@""]) {
            //No image found, show initial view
            self.senderInitialNameView.alpha = 1.0f;
            self.senderImageView.alpha = 0.0f;
            self.senderInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:activeUser.fullname];
            self.senderInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:activeUser.fullname isGroup:NO];
        }
        else {
            self.senderInitialNameView.alpha = 0.0f;
            self.senderImageView.alpha = 1.0f;
            [self.senderImageView setImageWithURLString:senderImageURL];
        }
        
        if (recipientImageURL == nil || [recipientImageURL isEqualToString:@""]) {
            self.recipientInitialNameView.alpha = 1.0f;
            self.recipientImageView.alpha = 0.0f;

            BOOL isGroup;
            if (self.currentRoom.type == RoomTypeGroup) {
                isGroup = YES;
            }
            self.recipientInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name];
            self.recipientInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup];
        }
        else {
            self.recipientInitialNameView.alpha = 0.0f;
            self.recipientImageView.alpha = 1.0f;
            [self.recipientImageView setImageWithURLString:recipientImageURL];
        }
        
        self.senderImageView.layer.borderWidth = 4.0f;
        self.senderImageView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor;
        self.senderImageView.layer.cornerRadius = CGRectGetHeight(self.senderImageView.frame) / 2.0f;
        self.senderImageView.backgroundColor = [UIColor clearColor];
        self.senderImageView.contentMode = UIViewContentModeScaleAspectFill;
        
        self.recipientImageView.layer.borderWidth = 4.0f;
        self.recipientImageView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor;
        self.recipientImageView.layer.cornerRadius = CGRectGetHeight(self.senderImageView.frame) / 2.0f;
        self.recipientImageView.backgroundColor = [UIColor clearColor];
        self.recipientImageView.contentMode = UIViewContentModeScaleAspectFill;
        
        self.senderInitialNameView.layer.borderWidth = 4.0f;
        self.senderInitialNameView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor;
        self.senderInitialNameView.layer.cornerRadius = CGRectGetWidth(self.senderInitialNameView.frame) / 2.0f;
        self.senderInitialNameView.clipsToBounds = YES;

        UIFont *initialNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRoomAvatarLargeLabel];
        UIColor *initialNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorRoomAvatarLargeLabel];
        
        self.senderInitialNameLabel.textColor = initialNameLabelColor;
        self.senderInitialNameLabel.font = initialNameLabelFont;
        
        self.recipientInitialNameView.layer.borderWidth = 4.0f;
        self.recipientInitialNameView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor;
        self.recipientInitialNameView.layer.cornerRadius = CGRectGetWidth(self.recipientInitialNameView.frame) / 2.0f;
        self.recipientInitialNameView.clipsToBounds = YES;

        self.recipientInitialNameLabel.textColor = initialNameLabelColor;
        self.recipientInitialNameLabel.font = initialNameLabelFont;
        
        [UIView animateWithDuration:0.0f animations:^{
            self.emptyView.alpha = 1.0f;
        }];
    }
    else {
        if (self.emptyView.alpha == 0.0f) {
            return;
        }
        
        //hide empty chat
        [UIView animateWithDuration:0.2f animations:^{
            self.emptyView.alpha = 0.0f;
        }];
    }
}

- (void)popUpInfoTappedSingleButtonOrRightButtonWithIdentifier:(NSString *)popupIdentifier {
    [super popUpInfoTappedSingleButtonOrRightButtonWithIdentifier:popupIdentifier];
    
    if ([popupIdentifier isEqualToString:@"Error File Size Excedeed"]) {
        [self showInputAccessoryView];
    }
    else if ([popupIdentifier isEqualToString:@"Error Delete Message"]) {
        [self showInputAccessoryView];
    }
    else if ([popupIdentifier isEqualToString:@"Long Press Save Image"]) {
        //Do nothing because hide popup handled when we press the button
        [self showInputAccessoryView];
    }
    else if ([popupIdentifier isEqualToString:@"Long Press Save Video"]) {
        //Do nothing because hide popup handled when we press the button
        [self showInputAccessoryView];
    }
    else if ([popupIdentifier isEqualToString:@"Error Delete Group Manually"]) {

    }
}

- (IBAction)sendButtonDidTapped:(id)sender {
    if ([self.messageArray count] != 0) {
        [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
    }
    
    //Remove unread button
    [TAPUtil performBlock:^{
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
    } afterDelay:1.0f];
    
    //Hide unread message indicator top view
    if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) {
        [TAPUtil performBlock:^{
            [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
        } afterDelay:1.0f];
    }
    
    //Remove highlighted message.
    NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    id cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        if ([cell isKindOfClass:[TAPMyChatBubbleTableViewCell class]]) {
            TAPMyChatBubbleTableViewCell *myChatCell = cell;
            [myChatCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:self.selectedMessage];
        }
        else if ([cell isKindOfClass:[TAPYourChatBubbleTableViewCell class]]) {
            TAPYourChatBubbleTableViewCell *yourChatCell = cell;
            [yourChatCell showStatusLabel:NO animated:YES];
        }
        [cell layoutIfNeeded];
    } completion:^(BOOL finished) {
        //completion
    }];
    
    //remove selectedMessage
    self.selectedMessage = [TAPMessageModel new];
    
    NSString *currentMessage = [TAPUtil nullToEmptyString:self.messageTextView.text];
    currentMessage = [currentMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    if (![currentMessage isEqualToString:@""]) {
        [[TAPChatManager sharedManager] sendTextMessage:currentMessage];
        self.messageTextView.text = @"";
    }
    else {
        
        //Check if forward message exist, send forward message
        TAPChatManagerQuoteActionType quoteActionType =  [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID];
        
        if (quoteActionType == TAPChatManagerQuoteActionTypeForward) {
            [[TAPChatManager sharedManager] checkAndSendForwardedMessageWithRoom:self.currentRoom];
        }
        
        self.messageTextView.text = @"";
    }
    
    if(self.currentInputAccessoryExtensionHeight > 0.0f) {
        [self showInputAccessoryExtensionView:NO];
    }
    
    if(self.tableView.contentOffset.y != 0 && [self.messageArray count] != 0) {
        //        Only scroll if table view is at bottom
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
    
    [self checkEmptyState];
    [[TAPChatManager sharedManager] stopTyping];
}

- (void)backButtonDidTapped {
    [self.lastSeenTimer invalidate];
    _lastSeenTimer = nil;
    [self destroySequence];
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)profileImageDidTapped {
    
    //reject if deletedRoomView exist
    if (self.deletedRoomView.alpha == 1.0f || self.kickedGroupRoomBackgroundView.alpha == 1.0f) {
        return;
    }
    
    [TAPUtil performBlock:^{
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
    } afterDelay:1.0f];
    
    [self setKeyboardStateDefault];
    
    NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID];
    TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID];
    //CS NOTE - add resign first responder before every pushVC to handle keyboard height
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
    
    if (self.currentRoom.type == RoomTypePersonal) {
        id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
        if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomProfileButtonTapped:otherUser:room:currentShownNavigationController:)]) {
            [tapUIChatRoomDelegate tapTalkChatRoomProfileButtonTapped:self otherUser:otherUser room:self.currentRoom currentShownNavigationController:self.navigationController];
        }
        else {
            TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init];
            profileViewController.room = self.currentRoom;
            profileViewController.otherUserID = otherUser.userID;
            profileViewController.delegate = self;
            [self.navigationController pushViewController:profileViewController animated:YES];
        }
    }
    else if (self.currentRoom.type == RoomTypeGroup) {
        id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
        if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkGroupChatRoomProfileButtonTapped:room:currentShownNavigationController:)]) {
            [tapUIChatRoomDelegate tapTalkGroupChatRoomProfileButtonTapped:self room:self.currentRoom currentShownNavigationController:self.navigationController];
        }
        else {
            TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init];
            profileViewController.room = self.currentRoom;
            profileViewController.otherUserID = otherUser.userID;
            profileViewController.delegate = self;
            [self.navigationController pushViewController:profileViewController animated:YES];
        }
    }
}

- (void)openUserProfileFromGroupChatWithMessage:(TAPMessageModel *)tappedMessage {

    //reject if deletedRoomView exist
    if (self.deletedRoomView.alpha == 1.0f || self.kickedGroupRoomBackgroundView.alpha == 1.0f) {
        return;
    }
    
    //Client implement the delegate for handle tap profile user
    id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate;
    if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkGroupMemberAvatarTappedWithRoom:user:currentShownNavigationController:)]) {
        [tapUIChatRoomDelegate tapTalkGroupMemberAvatarTappedWithRoom:tappedMessage.room user:tappedMessage.user currentShownNavigationController:self.navigationController];
        return;
    }
    
    TAPUserModel *otherUser = tappedMessage.user;
    
    [TAPUtil performBlock:^{
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES];
    } afterDelay:1.0f];
    
    [self setKeyboardStateDefault];
    
    //CS NOTE - add resign first responder before every pushVC to handle keyboard height
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
    
   if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomProfileButtonTapped:otherUser:room:currentShownNavigationController:)]) {
       [tapUIChatRoomDelegate tapTalkChatRoomProfileButtonTapped:self otherUser:otherUser room:self.currentRoom currentShownNavigationController:self.navigationController];
   }
   else {
       TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init];
       profileViewController.room = self.currentRoom;
       profileViewController.user = otherUser;
       profileViewController.delegate = self;
       profileViewController.tapProfileViewControllerType = TAPProfileViewControllerTypeGroupMemberProfile;
       [self.navigationController pushViewController:profileViewController animated:YES];
   }
}

- (IBAction)topFloatingIndicatorButtonDidTapped:(id)sender {
    if (self.isTopFloatingIndicatorLoading || self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeLoading) {
        return;
    }
    
    _isTopFloatingIndicatorLoading = YES;
    if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage) {
        [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:NO];
        [self scrollToFirstUnreadMessage];
    }
}

- (IBAction)handleTapOnTableView:(UITapGestureRecognizer *)gestureRecognizer {
    [self.keyboardViewController setKeyboardHeight:0.0f];
    [UIView animateWithDuration:0.2f animations:^{
        self.secondaryTextField.inputView.frame = CGRectMake(CGRectGetMinX(self.secondaryTextField.inputView.frame), 0.0f, CGRectGetWidth(self.secondaryTextField.inputView.frame), CGRectGetHeight(self.secondaryTextField.inputView.frame));
    }];
    
    //set keyboard state to default
    [self setKeyboardStateDefault];
    
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
}

- (IBAction)chatAnchorButtonDidTapped:(id)sender {
    NSInteger numberOfPendingArray = [self.scrolledPendingMessageArray count];
    
    if (numberOfPendingArray > 0) {
        //Add pending message to messageArray (pending message has previously inserted in messageDictionary in didReceiveNewMessage)
        [self.messageArray insertObjects:self.scrolledPendingMessageArray atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfPendingArray)]];
        
        [self.scrolledPendingMessageArray removeAllObjects];
        [self.tableView reloadData];
        
        //        //Uncommand to scroll to top unread message (and command scroll to index 0 below) - have some glitch if scrolled pending message height is bigger than current y offset
        //        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfPendingArray - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
        
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
    else {
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
}

- (void)checkAnchorUnreadLabel {
    if ([self.anchorUnreadMessageArray count] <= 0) {
        if (self.chatAnchorBadgeView.alpha != 0.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorBadgeView.alpha = 0.0f;
            }];
        }
        
        self.chatAnchorBadgeLabel.text = @"0";
    }
    else {
        if (self.chatAnchorBadgeView.alpha != 1.0f && self.chatAnchorBackgroundView.alpha == 1.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorBadgeView.alpha = 1.0f;
            }];
        }
        
        self.chatAnchorBadgeLabel.text = [NSString stringWithFormat:@"%li", [self.anchorUnreadMessageArray count]];
    }
}

- (void)timerRefreshLastSeen {
    NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f;
    [self updateLastSeenWithTimestamp:currentLastSeen];
}

- (void)updateLastSeenWithTimestamp:(NSTimeInterval)timestamp {
    
    if (self.currentRoom.type != RoomTypePersonal) {
        return;
    }
    
    NSDate *date = [NSDate date];
    NSTimeInterval timeInterval = [date timeIntervalSince1970];
    
    NSTimeInterval timeGap = timeInterval - timestamp;
    
    NSDateFormatter *midnightDateFormatter = [[NSDateFormatter alloc] init];
    midnightDateFormatter.dateFormat = @"dd-MMM-yyyy";
    NSString *midnightFormattedCreatedDate = [midnightDateFormatter stringFromDate:date];
    
    NSDate *todayMidnightDate = [midnightDateFormatter dateFromString:midnightFormattedCreatedDate];
    NSTimeInterval midnightTimeInterval = [todayMidnightDate timeIntervalSince1970];
    
    NSTimeInterval midnightTimeGap = timeInterval - midnightTimeInterval;
    
    NSString *lastSeenString = @"";
    
    [self isShowOnlineDotStatus:NO];
    
    if (self.onlineStatus.isOnline) {
        lastSeenString = NSLocalizedString(@"Active now", @"");
        [self isShowOnlineDotStatus:YES];
    }
    else if (timestamp == 0) {
        lastSeenString = @"";
    }
    else if (timeGap <= midnightTimeGap) {
        if (timeGap < 60.0f) {
            //Set recently
            lastSeenString = NSLocalizedString(@"Active recently", @"");
        }
        else if (timeGap < 3600.0f) {
            //Set minutes before
            NSInteger numberOfMinutes = floor(timeGap/60.0f);
            
            NSString *minuteString = NSLocalizedString(@"minutes", @"");
            
            if (timeGap < 120.0f) {
                minuteString = NSLocalizedString(@"minute", @"");
            }
            
            lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"Active %li %@ ago", @""), (long)numberOfMinutes, minuteString];
        }
        else {
            //Set hour before
            NSInteger numberOfHours = round(timeGap/3600.0f);
            
            NSString *hourString = NSLocalizedString(@"hours", @"");
            
            if (timeGap < 120.0f) {
                hourString = NSLocalizedString(@"hour", @"");
            }
            
            lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"Active %li %@ ago", @""), (long)numberOfHours, hourString];
        }
    }
    else if (timeGap <= 86400.0f + midnightTimeGap) {
        //Set yesterday
        lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"Active yesterday", @"")];
    }
    else if (timeGap <= 86400.0f * 6 + midnightTimeGap) {
        //Set days ago
        
        NSInteger numberOfDays = floor(timeGap/86400.0f);
        
        if (numberOfDays == 0) {
            numberOfDays = 1;
        }
        
        NSString *dayString = NSLocalizedString(@"days", @"");
        
        lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"Active %li %@ ago", @""), (long)numberOfDays, dayString];
        
        if (timeGap <= 86400.0f || numberOfDays == 1) {
            lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"Active yesterday", @"")];
        }
    }
    else if (timeGap <= 86400.0f*7 + midnightTimeGap) {
        //Set a week ago
        lastSeenString = @"Active a week ago";
    }
    else {
        //Set date
        NSDate *lastLoginDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
        
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.dateFormat = @"dd MMM YYYY";
        NSString *formattedCreatedDate = [dateFormatter stringFromDate:lastLoginDate];
        
        lastSeenString = [NSString stringWithFormat:@"Last active %@", formattedCreatedDate];
    }
    
    self.userStatusLabel.text = lastSeenString;
    [self.userStatusLabel sizeToFit];
    self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f);
    CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f;
    self.userDescriptionView.frame = CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f);
    self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y);
}

- (void)isShowOnlineDotStatus:(BOOL)isShow {
    if (isShow) {
        self.userStatusView.frame = CGRectMake(0.0f, (16.0f - 7.0f) / 2.0f + 1.6f, 7.0f, 7.0f);
        self.userStatusView.alpha = 1.0f;
        self.userStatusLabel.frame = CGRectMake(CGRectGetMaxX(self.userStatusView.frame) + 4.0f, 0.0f, 0.0f, 16.0f);
    }
    else {
        self.userStatusView.frame = CGRectZero;
        self.userStatusView.alpha = 0.0f;
        self.userStatusLabel.frame = CGRectMake(0.0f, 0.0f, 0.0f, 16.0f);
    }
}

- (void)setAsTyping:(BOOL)typing {
    if(typing) {
        [self refreshTypingLabelState];
        self.userTypingView.alpha = 1.0f;
        self.userDescriptionView.alpha = 0.0f;
        [self performSelector:@selector(setAsTypingNoAfterDelay) withObject:nil afterDelay:15.0f];
    }
    else {
        self.userTypingView.alpha = 0.0f;
        self.userDescriptionView.alpha = 1.0f;
    }
}

- (void)setAsTypingNoAfterDelay {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(setAsTypingNoAfterDelay) object:nil];
    self.userTypingView.alpha = 0.0f;
    self.userDescriptionView.alpha = 1.0f;
}

- (void)showLoadMoreMessageLoadingView:(BOOL)show
                              withType:(LoadMoreMessageViewType)type {
    
    if (show) {
        self.loadMoreMessageViewHeight = 20.0f;
        
        if (type == LoadMoreMessageViewTypeOlderMessage) {
            self.loadMoreMessageLoadingLabel.text = NSLocalizedString(@"Loading Older Messages", @"");
        }
        else if (type == LoadMoreMessageViewTypeNewMessage) {
            self.loadMoreMessageLoadingLabel.text = NSLocalizedString(@"Loading New Messages", @"");
        }
        
        [UIView animateWithDuration:0.2f animations:^{
            //change frame
            self.loadMoreMessageLoadingHeightConstraint.constant = self.loadMoreMessageViewHeight;
            [self.view layoutIfNeeded];
        }];
        
        if ([self.loadMoreMessageLoadingViewImageView.layer animationForKey:@"SpinAnimation"] == nil) {
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
            animation.fromValue = [NSNumber numberWithFloat:0.0f];
            animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
            animation.duration = 1.5f;
            animation.repeatCount = INFINITY;
            animation.removedOnCompletion = NO;
            [self.loadMoreMessageLoadingViewImageView.layer addAnimation:animation forKey:@"SpinAnimation"];
        }
    }
    else {
        self.loadMoreMessageViewHeight = 0.0f;
        
        [UIView animateWithDuration:0.2f animations:^{
            //change frame
            self.loadMoreMessageLoadingHeightConstraint.constant = self.loadMoreMessageViewHeight;
            [self.view layoutIfNeeded];
        }];
        
        //Remove Animation
        if ([self.loadMoreMessageLoadingViewImageView.layer animationForKey:@"SpinAnimation"] != nil) {
            [self.loadMoreMessageLoadingViewImageView.layer removeAnimationForKey:@"SpinAnimation"];
        }
    }
    
    CGFloat currentHeight = self.loadMoreMessageViewHeight;
    if (self.connectionStatusHeight == 0.0f && self.loadMoreMessageViewHeight== 0.0f) {
        currentHeight = 0.0f;
    }
    else if (self.connectionStatusHeight > 0.0f) {
        currentHeight = self.connectionStatusHeight;
    }
    else if (self.loadMoreMessageViewHeight > 0.0f) {
        currentHeight = self.loadMoreMessageViewHeight;
    }
    
    [UIView animateWithDuration:0.2f animations:^{
        //change frame
        self.tableViewTopConstraint.constant = currentHeight - 50.0f;
        [self.view layoutIfNeeded];
    }];
}

- (void)showTopFloatingIdentifierView:(BOOL)show
                             withType:(TopFloatingIndicatorViewType)type
               numberOfUnreadMessages:(NSInteger)numberOfUnreadMessages
                             animated:(BOOL)animated {
    _topFloatingIndicatorViewType = type;
    _isShowingTopFloatingIdentifier = show;
    if (type == TopFloatingIndicatorViewTypeUnreadMessage) {
        if (numberOfUnreadMessages != 0) {
            NSString *unreadMessagesString = @"";
            if (numberOfUnreadMessages > 99) {
                unreadMessagesString = @"99+";
            }
            else {
                unreadMessagesString = [NSString stringWithFormat:@"%ld", (long)numberOfUnreadMessages];
            }
            self.topFloatingIndicatorLabel.text = [NSString stringWithFormat:@"%@ unread messages", unreadMessagesString];
            self.topFloatingIndicatorImageView.image = [UIImage imageNamed:@"TAPIconUnreadMessageTop" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
            self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomFloatingUnreadButton]];
        }
    }
    else if (type == TopFloatingIndicatorViewTypeLoading) {
        self.topFloatingIndicatorLabel.text = NSLocalizedString(@"Loading", @"");
        self.topFloatingIndicatorImageView.image = [UIImage imageNamed:@"TAPIconLoaderProgress" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
        self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconLoadingProgressPrimary]];
    }
    
    CGSize labelSize = [self.topFloatingIndicatorLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, 16.0f)];
    self.topFloatingIndicatorWidthConstraint.constant = labelSize.width;
    
    if (animated) {
        if (show) {
            [UIView animateWithDuration:0.2f animations:^{
                self.topFloatingIndicatorView.alpha = 1.0f;
                
                if (type == TopFloatingIndicatorViewTypeLoading) {
                    //Remove Existing Animation
                    if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                        [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"];
                    }
                    //Add Animation
                    if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] == nil) {
                        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
                        animation.fromValue = [NSNumber numberWithFloat:0.0f];
                        animation.toValue = [NSNumber numberWithFloat: 2 * M_PI];
                        animation.duration = 1.5f;
                        animation.repeatCount = INFINITY;
                        animation.removedOnCompletion = NO;
                        [self.topFloatingIndicatorImageView.layer addAnimation:animation forKey:@"SpinAnimation"];
                    }
                }
            }];
        }
        else {
            _isTopFloatingIndicatorLoading = NO;
            [UIView animateWithDuration:0.2f animations:^{
                self.topFloatingIndicatorView.alpha = 0.0f;
                
                if (type == TopFloatingIndicatorViewTypeLoading) {
                    //Remove Animation
                    if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                        [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"];
                    }
                }
            }];
        }
    }
    else {
        if (show) {
            self.topFloatingIndicatorView.alpha = 1.0f;
            
            if (type == TopFloatingIndicatorViewTypeLoading) {
                //Remove Existing Animation
                if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                    [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"];
                }
                //Add Animation
                if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] == nil) {
                    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
                    animation.fromValue = [NSNumber numberWithFloat:0.0f];
                    animation.toValue = [NSNumber numberWithFloat: 2 * M_PI];
                    animation.duration = 1.5f;
                    animation.repeatCount = INFINITY;
                    animation.removedOnCompletion = NO;
                    [self.topFloatingIndicatorImageView.layer addAnimation:animation forKey:@"SpinAnimation"];
                }
            }
        }
        else {
            _isTopFloatingIndicatorLoading = NO;
            self.topFloatingIndicatorView.alpha = 0.0f;
            
            if (type == TopFloatingIndicatorViewTypeLoading) {
                //Remove Animation
                if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                    [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"];
                }
            }
        }
    }
}

- (void)checkAndRefreshOnlineStatus {
    if([[TAPChatManager sharedManager] checkShouldRefreshOnlineStatus]) {
        [self setAsTyping:NO];
        NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID];
        
        if (self.currentRoom.type == RoomTypePersonal) {
            [TAPDataManager callAPIGetUserByUserID:otherUserID success:^(TAPUserModel *user) {
                
                self.inputMessageAccessoryView.alpha = 1.0f;
                
                //Upsert User to Contact Manager
                [[TAPContactManager sharedManager] addContactWithUserModel:user saveToDatabase:NO];
                
                BOOL isTyping = [[TAPChatManager sharedManager] checkIsTypingWithRoomID:self.currentRoom.roomID];
                [self setAsTyping:isTyping];
                
                TAPOnlineStatusModel *onlineStatus = [TAPOnlineStatusModel new];
                onlineStatus.isOnline = user.isOnline;
                onlineStatus.lastActive = user.lastActivity;
                
                _onlineStatus = onlineStatus;
                
                NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f;
                [self updateLastSeenWithTimestamp:currentLastSeen];
                
                //Used to check if need to show add to contact view or not
                _otherUser = user;
                _isOtherUserIsContact = user.isContact;
                [self checkAndSetupAddToContactsView];
                
            } failure:^(NSError *error) {
                if (error.code == 40401) {
                    //user not found
                    //hide textview
                    self.inputMessageAccessoryView.alpha = 0.0f;
                    [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
                }
            }];
        }
        else {
            BOOL isTyping = [[TAPChatManager sharedManager] checkIsTypingWithRoomID:self.currentRoom.roomID];
            [self setAsTyping:isTyping];
            
            TAPRoomModel *room = [[TAPGroupManager sharedManager] getRoomWithRoomID:self.currentRoom.roomID];
            if (room != nil) {
                _currentRoom = room;
                [self refreshRoomStatusUIInfo];
            }
            
            [TAPDataManager callAPIGetRoomWithRoomID:self.currentRoom.roomID success:^(TAPRoomModel *room) {
                _currentRoom = room;
                [self refreshRoomStatusUIInfo];
            } failure:^(NSError *error) {
                if (error.code == 40401) {
                    //user not found
                    //hide textview
                    self.inputMessageAccessoryView.alpha = 0.0f;
                    [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES];
                }
            }];
        }
    }
}

- (void)fetchUnreadMessagesDataWithSuccess:(void (^)(NSArray *unreadMessages))success
                                   failure:(void (^)(NSError *error))failure {
    [TAPDataManager getDatabaseUnreadMessagesInRoomWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID
                                                 activeUserID:[TAPChatManager sharedManager].activeUser.userID
                                                      success:^(NSArray *unreadMessages) {
                                                          success(unreadMessages);
                                                      } failure:^(NSError *error) {
                                                          failure(error);
                                                      }];
}

- (void)scrollToFirstUnreadMessage {
    NSString *localID = self.unreadLocalID;
    localID = [TAPUtil nullToEmptyString:localID];
    if (![localID isEqualToString:@""]) {
        TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        if (!currentMessage.isDeleted && !currentMessage.isHidden) {
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
        }
        [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
    }
}

- (void)scrollToMessageAndLoadDataWithLocalID:(NSString *)localID {
    TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
    if (currentMessage) {
        NSArray *messageArray = [self.messageArray copy];
        NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];
        
        self.tappedMessageLocalID = @"";
        
        if (!currentMessage.isDeleted && !currentMessage.isHidden) {
            [TAPUtil performBlock:^{
                [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
            } afterDelay:0.2f];
        }
        [TAPUtil performBlock:^{
             [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
        } afterDelay:0.2f];
       
    }
    else {
        if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) {
            [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES];
        }
        self.tappedMessageLocalID = localID;
        [self retrieveExistingMessages];
    }
}
-(BOOL)checkIsRowVisibleWithRowIndex:(NSInteger)rowIndex {
    NSArray *indexes = [self.tableView indexPathsForVisibleRows];
    for (NSIndexPath *index in indexes) {
        if (index.row == rowIndex) {
            return YES;
        }
    }
    return NO;
}

- (void)checkAndShowUnreadButton {
    if (!self.isUnreadButtonShown) {
        _isUnreadButtonShown = YES;
        [self performSelector:@selector(showUnreadButton) withObject:nil afterDelay:1.0f];
    }
}

- (void)showUnreadButton {
    if (self.numberOfUnreadMessages != 0) {
        //Show unread message view
        NSString *obtainedLocalID = self.unreadLocalID;
        TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:obtainedLocalID];
        NSInteger unreadMessageIndex = [self.messageArray indexOfObject:obtainedMessage];
        if(NSNotFound != unreadMessageIndex) {
            NSInteger rowIndex = unreadMessageIndex + 1; // +1 because unread identifier cell is above earliest unread message
            BOOL isVisible = [self checkIsRowVisibleWithRowIndex:rowIndex];
            if (!isVisible) {
                //Show top floating unread identifier only if unread message bar is not visible
                [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:self.numberOfUnreadMessages animated:NO];
            }
        }
        else {
            //Unread not found
            _isUnreadButtonShown = NO;
        }
    }
    else {
        //Unread not found
        _isUnreadButtonShown = NO;
    }
}

- (void)showLoadMessageCellLoading:(BOOL)show {
    if (show) {
        if (self.isLoadingOldMessageFromAPI || [self.messageArray count] == 0 || self.isShowingTopFloatingIdentifier) {
            return;
        }
        _lastLoadingCellRowPosition = [self.messageArray count];
        //insert cell at last row
        _isLoadingOldMessageFromAPI = YES;
        NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:self.lastLoadingCellRowPosition inSection:0];
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];
    }
    else {
        if (!self.isLoadingOldMessageFromAPI || [self.messageArray count] == 0 || self.isShowingTopFloatingIdentifier) {
            return;
        }
        //remove cell at last row
        _isLoadingOldMessageFromAPI = NO;
        NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:self.lastLoadingCellRowPosition inSection:0];
        if (self.lastLoadingCellRowPosition >= [self.messageArray count]) {
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationNone];
            [self.tableView endUpdates];
        }
    }
}

//Override completionSelector method of save image to gallery
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeSuccessMessage popupIdentifier:@"Long Press Save Image"  title:NSLocalizedString(@"Success", @"") detailInformation:NSLocalizedString(@"Image saved successfully",@"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
}

//Override completionSelector method of save video to gallery
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeSuccessMessage popupIdentifier:@"Long Press Save Video"  title:NSLocalizedString(@"Success", @"") detailInformation:NSLocalizedString(@"Video saved successfully",@"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
}

- (void)showDeleteMessageActionWithMessageArray:(NSString *)deletedMessageIDArray {

    [self showInputAccessoryView];
    //Temporary delete for everyone
    //Delete For Everyone
    [TAPDataManager callAPIDeleteMessageWithMessageIDs:deletedMessageIDArray roomID:[TAPChatManager sharedManager].activeRoom.roomID isDeletedForEveryone:YES success:^(NSArray *deletedMessageIDArray) {
        
    } failure:^(NSError *error) {
        [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message"  title:NSLocalizedString(@"Sorry", @"") detailInformation:NSLocalizedString(@"Failed to delete message, please try again.",@"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
    }];
    
    //DV Note - Uncomment this section later to show delete for me or everyone
    //    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    //    UIAlertAction *deleteForMeAction = [UIAlertAction
    //                                        actionWithTitle:@"Delete for Me"
    //                                        style:UIAlertActionStyleDestructive
    //                                        handler:^(UIAlertAction * action) {
    //                                            //Delete For Me
    //                                            [TAPDataManager callAPIDeleteMessageWithMessageIDs:deletedMessageIDArray roomID:[TAPChatManager sharedManager].activeRoom.roomID isDeletedForEveryone:NO success:^(NSArray *deletedMessageIDArray) {
    //
    //                                            } failure:^(NSError *error) {
    //                                                [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message"  title:NSLocalizedString(@"Sorry", @"") detailInformation:NSLocalizedString(@"Failed to delete message, please try again.",@"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
    //                                            }];
    //                                        }];
    //
    //    UIAlertAction *deleteForEveryoneAction = [UIAlertAction
    //                                              actionWithTitle:@"Delete For Everyone"
    //                                              style:UIAlertActionStyleDestructive
    //                                              handler:^(UIAlertAction * action) {
    //                                                  //Delete For Everyone
    //                                                  [TAPDataManager callAPIDeleteMessageWithMessageIDs:deletedMessageIDArray roomID:[TAPChatManager sharedManager].activeRoom.roomID isDeletedForEveryone:YES success:^(NSArray *deletedMessageIDArray) {
    //                                                      NSLog(@"DELETE MESSAGE ID ARRAY: %@", [deletedMessageIDArray description]);
    //                                                  } failure:^(NSError *error) {
    //                                                      [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message"  title:NSLocalizedString(@"Sorry", @"") detailInformation:NSLocalizedString(@"Failed to delete message, please try again.",@"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
    //
    //                                                  }];
    //                                              }];
    //
    //    UIAlertAction *cancelAction = [UIAlertAction
    //                                   actionWithTitle:@"Cancel"
    //                                   style:UIAlertActionStyleCancel
    //                                   handler:^(UIAlertAction * action) {
    //
    //                                   }];
    //    [cancelAction setValue:[TAPUtil getColor:TAP_COLOR_PRIMARY_COLOR_1] forKey:@"titleTextColor"];
    //
    //    [alertController addAction:deleteForMeAction];
    //    [alertController addAction:deleteForEveryoneAction];
    //    [alertController addAction:cancelAction];
    //
    //    [self presentViewController:alertController animated:YES completion:nil];
}

//DV NOTE - Uncomment this to use API download thumbnail image
//- (void)fetchImageDataWithMessage:(TAPMessageModel *)message {
//
//    [[TAPFileDownloadManager sharedManager] receiveImageDataWithMessage:message progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) {
//        dispatch_async(dispatch_get_main_queue(), ^{
//            TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
//            NSString *currentActiveRoomID = currentRoom.roomID;
//            currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
//
//            NSString *roomID = receivedMessage.room.roomID;
//            roomID = [TAPUtil nullToEmptyString:roomID];
//
//            NSString *localID = receivedMessage.localID;
//            localID = [TAPUtil nullToEmptyString:localID];
//
//            if (![roomID isEqualToString:currentActiveRoomID]) {
//                return;
//            }
//
//            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
//NSArray *messageArray = [self.messageArray copy];
//NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];//
//            TAPChatMessageType type = currentMessage.type;
//            if (type == TAPChatMessageTypeImage) {
//
//                if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
//                    //My Chat
//                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateProgressUploadingImageWithProgress:progress total:total];
//                }
//                else {
//                    //Their Chat
//                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateProgressDownloadingImageWithProgress:progress total:total];
//                }
//            }
//        });
//    } successThumbnailImage:^(UIImage * _Nonnull thumbnailImage, TAPMessageModel * _Nonnull receivedMessage) {
//        dispatch_async(dispatch_get_main_queue(), ^{
//            TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
//            NSString *currentActiveRoomID = currentRoom.roomID;
//            currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
//
//            NSString *roomID = receivedMessage.room.roomID;
//            roomID = [TAPUtil nullToEmptyString:roomID];
//
//            NSString *localID = receivedMessage.localID;
//            localID = [TAPUtil nullToEmptyString:localID];
//
//            if (![roomID isEqualToString:currentActiveRoomID]) {
//                return;
//            }
//
//            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
//NSArray *messageArray = [self.messageArray copy];
//NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];//
//            TAPChatMessageType type = currentMessage.type;
//            if (type == TAPChatMessageTypeImage) {
//                if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
//                    //My Chat
//                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    if (thumbnailImage != nil) {
//                        [cell setThumbnailImage:thumbnailImage];
//                    }
//                    [cell animateFinishedUploadingImage];
//                }
//                else {
//                    //Their Chat
//                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    if (thumbnailImage != nil) {
//                        [cell setThumbnailImage:thumbnailImage];
//                    }
//                    [cell animateFinishedDownloadingImage];
//                }
//            }
//        });
//    } successFullImage:^(UIImage * _Nonnull fullImage, TAPMessageModel * _Nonnull receivedMessage) {
//        dispatch_async(dispatch_get_main_queue(), ^{
//            TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
//            NSString *currentActiveRoomID = currentRoom.roomID;
//            currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
//
//            NSString *roomID = receivedMessage.room.roomID;
//            roomID = [TAPUtil nullToEmptyString:roomID];
//
//            NSString *localID = receivedMessage.localID;
//            localID = [TAPUtil nullToEmptyString:localID];
//
//            if (![roomID isEqualToString:currentActiveRoomID]) {
//                return;
//            }
//
//            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
//NSArray *messageArray = [self.messageArray copy];
//NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];//
//            TAPChatMessageType type = currentMessage.type;
//            if (type == TAPChatMessageTypeImage) {
//                if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
//                    //My Chat
//                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    if (fullImage != nil) {
//                        [cell setFullImage:fullImage];
//                    }
//                    [cell animateFinishedUploadingImage];
//                }
//                else {
//                    //Their Chat
//                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    if (fullImage != nil) {
//                        [cell setFullImage:fullImage];
//                    }
//                    [cell animateFinishedDownloadingImage];
//                }
//            }
//        });
//    } failureThumbnailImage:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) {
//        dispatch_async(dispatch_get_main_queue(), ^{
//            TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
//            NSString *currentActiveRoomID = currentRoom.roomID;
//            currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
//
//            NSString *roomID = receivedMessage.room.roomID;
//            roomID = [TAPUtil nullToEmptyString:roomID];
//
//            NSString *localID = receivedMessage.localID;
//            localID = [TAPUtil nullToEmptyString:localID];
//
//            if (![roomID isEqualToString:currentActiveRoomID]) {
//                return;
//            }
//
//            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
//            NSInteger currentRowIndex = [self.messageArray indexOfObject:currentMessage];
//
//            TAPChatMessageType type = currentMessage.type;
//            if (type == TAPChatMessageTypeImage) {
//
//                if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
//                    //My Chat
//                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateFailedUploadingImage];
//                }
//                else {
//                    //Their Chat
//                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateFailedDownloadingImage];
//                }
//            }
//        });
//    } failureFullImage:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) {
//        dispatch_async(dispatch_get_main_queue(), ^{
//            TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom;
//            NSString *currentActiveRoomID = currentRoom.roomID;
//            currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID];
//
//            NSString *roomID = receivedMessage.room.roomID;
//            roomID = [TAPUtil nullToEmptyString:roomID];
//
//            NSString *localID = receivedMessage.localID;
//            localID = [TAPUtil nullToEmptyString:localID];
//
//            if (![roomID isEqualToString:currentActiveRoomID]) {
//                return;
//            }
//
//            TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID];
//            NSInteger currentRowIndex = [self.messageArray indexOfObject:currentMessage];
//
//            TAPChatMessageType type = currentMessage.type;
//            if (type == TAPChatMessageTypeImage) {
//
//                if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
//                    //My Chat
//                    TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateFailedUploadingImage];
//                }
//                else {
//                    //Their Chat
//                    TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]];
//                    [cell animateFailedDownloadingImage];
//                }
//            }
//        });
//    }];
//}
//END DV NOTE

- (void)refreshRoomStatusUIInfo {
    TAPRoomModel *room = self.currentRoom;
    self.nameLabel.text = room.name;
    
    NSString *profileImageURL = room.imageURL.thumbnail;
    if (profileImageURL == nil || [profileImageURL isEqualToString:@""]) {
        BOOL isGroup;
        if (self.currentRoom.type == RoomTypeGroup) {
            isGroup = YES;
        }
        self.rightBarInitialNameView.alpha = 1.0f;
        self.rightBarImageView.alpha = 0.0f;
        self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name];
        self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup];
    }
    else {
        self.rightBarInitialNameView.alpha = 0.0f;
        self.rightBarImageView.alpha = 1.0f;
        [self.rightBarImageView setImageWithURLString:profileImageURL];
    }
    
    self.userStatusLabel.text = [NSString stringWithFormat:@"%ld Members", [self.currentRoom.participants count]];
    if ([self.currentRoom.participants count] == 0) {
        self.userStatusLabel.text = @"";
    }
    [self.userStatusLabel sizeToFit];
    self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f);
    CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f;
    self.userDescriptionView.frame = CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f);
    self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y);
}

- (void)refreshTypingLabelState {
    if (self.currentRoom.type == RoomTypePersonal) {
        self.typingLabel.text = NSLocalizedString(@"typing", @"");
    }
    else {
        NSDictionary *typingUserDictionary = [[TAPChatManager sharedManager] getTypingUsersWithRoomID:self.currentRoom.roomID];
        if ([typingUserDictionary count] == 0) {
            [self setAsTyping:NO];
        }
        else if ([typingUserDictionary count] == 1) {
            NSArray *values = [typingUserDictionary allValues];
            TAPUserModel *user = [values firstObject];
            NSString *fullName = user.fullname;
            NSArray *eachWordArray = [fullName componentsSeparatedByString:@" "];
            NSString *firstName = [eachWordArray objectAtIndex:0];
            self.typingLabel.text = [NSString stringWithFormat:@"%@ is typing", firstName];
        }
        else if ([typingUserDictionary count] > 1){
            self.typingLabel.text = [NSString stringWithFormat:@"%ld people are typing", [typingUserDictionary count]];
        }
    }
    [self.typingLabel sizeToFit];
    CGFloat typingLabelWidth = CGRectGetWidth(self.typingLabel.frame);
    if (typingLabelWidth > CGRectGetWidth([UIScreen mainScreen].bounds) - 64.0f - 64.0f) {
        typingLabelWidth = CGRectGetWidth([UIScreen mainScreen].bounds) - 64.0f - 64.0f;
    }
    self.typingLabel.frame = CGRectMake(20.0f, 0.0f, CGRectGetWidth(self.typingLabel.frame), 16.0f);
    
    self.userTypingView.frame = CGRectMake(CGRectGetMinX(self.userTypingView.frame), CGRectGetMinY(self.userTypingView.frame), CGRectGetMaxX(self.typingLabel.frame), CGRectGetHeight(self.userTypingView.frame));
    self.userTypingView.center = CGPointMake(self.nameLabel.center.x, self.userTypingView.center.y);
}

- (void)setDeleteRoomButtonAsLoading:(BOOL)loading animated:(BOOL)animated {
    if (loading) {
        
        self.navigationItem.leftBarButtonItem.enabled = NO;
        self.navigationItem.rightBarButtonItem.enabled = NO;
        
        if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
            self.navigationController.interactivePopGestureRecognizer.enabled = NO;
        }
        
        if (animated) {
            [UIView animateWithDuration:0.2f animations:^{
                self.deleteRoomButtonLabel.alpha = 0.0f;
                self.deleteRoomButtonIconImageView.alpha = 0.0f;
                self.deleteRoomButton.userInteractionEnabled = NO;
                
                self.deleteRoomButtonLoadingImageView.alpha = 1.0f;
            }];
            
            //ADD ANIMATION
            if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] == nil) {
                CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
                animation.fromValue = [NSNumber numberWithFloat:0.0f];
                animation.toValue = [NSNumber numberWithFloat:(2*M_PI)];
                animation.duration = 1.5f;
                animation.repeatCount = INFINITY;
                animation.cumulative = YES;
                animation.removedOnCompletion = NO;
                [self.deleteRoomButtonLoadingImageView.layer addAnimation:animation forKey:@"SpinAnimation"];
            }
        }
        else {
            self.deleteRoomButtonLabel.alpha = 0.0f;
            self.deleteRoomButtonIconImageView.alpha = 0.0f;
            self.deleteRoomButton.userInteractionEnabled = NO;
            
            self.deleteRoomButtonLoadingImageView.alpha = 1.0f;
            
            //ADD ANIMATION
            if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] == nil) {
                CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
                animation.fromValue = [NSNumber numberWithFloat:0.0f];
                animation.toValue = [NSNumber numberWithFloat:(2*M_PI)];
                animation.duration = 1.5f;
                animation.repeatCount = INFINITY;
                animation.cumulative = YES;
                animation.removedOnCompletion = NO;
                [self.deleteRoomButtonLoadingImageView.layer addAnimation:animation forKey:@"SpinAnimation"];
            }
        }
    }
    else {
        
        self.navigationItem.leftBarButtonItem.enabled = YES;
        self.navigationItem.rightBarButtonItem.enabled = YES;
        
        if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
            self.navigationController.interactivePopGestureRecognizer.enabled = YES;
        }
        
        if (animated) {
            [UIView animateWithDuration:0.2f animations:^{
                self.deleteRoomButtonLabel.alpha = 1.0f;
                self.deleteRoomButtonIconImageView.alpha = 1.0f;
                self.deleteRoomButton.userInteractionEnabled = YES;
                
                self.deleteRoomButtonLoadingImageView.alpha = 0.0f;
            }];
            
            //REMOVE ANIMATION
            if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                [self.deleteRoomButtonLoadingImageView.layer removeAnimationForKey:@"SpinAnimation"];
            }
        }
        else {
            self.deleteRoomButtonLabel.alpha = 1.0f;
            self.deleteRoomButtonIconImageView.alpha = 1.0f;
            self.deleteRoomButton.userInteractionEnabled = YES;
            
            self.deleteRoomButtonLoadingImageView.alpha = 0.0f;
            
            //REMOVE ANIMATION
            if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] != nil) {
                [self.deleteRoomButtonLoadingImageView.layer removeAnimationForKey:@"SpinAnimation"];
            }
        }
    }
}

- (IBAction)deleteGroupButtonDidTapped:(id)sender {
    //add sequence to delete message and physical files
    [self setDeleteRoomButtonAsLoading:YES animated:YES];
    [TAPDataManager deleteAllMessageAndPhysicalFilesInRoomWithRoomID:self.currentRoom.roomID success:^{
        
        if ([self.delegate respondsToSelector:@selector(chatViewControllerDidLeaveOrDeleteGroupWithRoom:)]) {
            [self.delegate chatViewControllerDidLeaveOrDeleteGroupWithRoom:self.currentRoom];
        }
        
        //Throw view to room list
        [TAPUtil performBlock:^{
            [self setDeleteRoomButtonAsLoading:NO animated:YES];
            [self.navigationController popToRootViewControllerAnimated:YES];
        } afterDelay:1.2f];
        
    } failure:^(NSError *error) {
        [self setDeleteRoomButtonAsLoading:NO animated:YES];
        NSString *errorMessage = [error.userInfo objectForKey:@"message"];
        errorMessage = [TAPUtil nullToEmptyString:errorMessage];
        [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Group Manually" title:NSLocalizedString(@"Failed", @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil];
    }];
}

- (void)showTapTalkMessageComposerView {
    [self showInputAccessoryView];
}

- (void)checkAndShowRoomViewState {
    //check if last message is deleted room
    TAPMessageModel *lastMessage = [self.messageArray firstObject];
    if (lastMessage.room.type == RoomTypePersonal && lastMessage.room.isDeleted) {
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) {
        //Check if system message with action remove participant and target user is current user
        //show deleted chat room view
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO];
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/delete"]) {
        [self.view endEditing:YES];
        if (lastMessage.room.type == RoomTypePersonal) {
            [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
        }
        else if (lastMessage.room.type == RoomTypeGroup) {
            [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES];
        }
    }
    else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/leave"] && [lastMessage.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
        [self.view endEditing:YES];
        [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO];
    }
}

//Add to Contacts View
- (IBAction)blockUserButtonDidTapped:(id)sender {
    //DV TODO - Add block user method here
}

- (IBAction)addContactButtonDidTapped:(id)sender {
    [TAPDataManager callAPIAddContactWithUserID:self.otherUser.userID success:^(NSString *message, TAPUserModel *user) {
        NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
        [dataDictionary setObject:[NSNumber numberWithBool:YES] forKey:self.currentRoom.roomID];
        [[NSUserDefaults standardUserDefaults] setSecureObject:dataDictionary forKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY];
        [[NSUserDefaults standardUserDefaults] synchronize];
        self.addToContactsViewHeightConstraint.constant = 0.0f;
        [UIView animateWithDuration:0.2f animations:^{
            self.addToContactContainerView.alpha = 0.0f;
        }];
    } failure:^(NSError *error) {
#ifdef DEBUG
        NSLog(@"%@", error);
#endif
        self.addToContactsViewHeightConstraint.constant = 0.0f;
        [UIView animateWithDuration:0.2f animations:^{
            self.addToContactContainerView.alpha = 0.0f;
        }];
    }];
}

- (IBAction)closeAddContactButtonDidTapped:(id)sender {
    NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
    [dataDictionary setObject:[NSNumber numberWithBool:YES] forKey:self.currentRoom.roomID];
    [[NSUserDefaults standardUserDefaults] setSecureObject:dataDictionary forKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY];
    [[NSUserDefaults standardUserDefaults] synchronize];
    self.addToContactsViewHeightConstraint.constant = 0.0f;
    [UIView animateWithDuration:0.2f animations:^{
        self.addToContactContainerView.alpha = 0.0f;
    }];
}

- (void)checkUpdatedUserProfileWithMessage:(TAPMessageModel *)message {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (message.type == TAPChatMessageTypeSystemMessage && ([message.action isEqualToString:@"user/update"] || [message.action isEqualToString:@"room/update"])) {
             TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
             NSString *updatedImageURLString = message.room.imageURL.thumbnail;
             NSString *currentImageURLString = room.imageURL.thumbnail;
             if (![updatedImageURLString isEqualToString:currentImageURLString]) {
                 if (updatedImageURLString == nil || [updatedImageURLString isEqualToString:@""]) {
                     BOOL isGroup;
                     if (message.room.type == RoomTypeGroup) {
                         isGroup = YES;
                     }
                     
                     self.rightBarInitialNameView.alpha = 1.0f;
                     self.rightBarImageView.alpha = 0.0f;
                     self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:message.room.name];
                     self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:message.room.name isGroup:isGroup];
                 }
                 else {
                     self.rightBarInitialNameView.alpha = 0.0f;
                     self.rightBarImageView.alpha = 1.0f;
                     [self.rightBarImageView setImageWithURLString:updatedImageURLString];
                 }
                 
                 self.nameLabel.text = message.room.name;
             }
         }
    });
}

@end