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

#import "TAPChatViewController.h"

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

#import "TAPMyChatBubbleTableViewCell.h"
#import "TAPMyImageBubbleTableViewCell.h"
#import "TAPYourChatBubbleTableViewCell.h"
#import "TAPConnectionStatusViewController.h"
#import "TAPKeyboardViewController.h"
#import "TAPProfileViewController.h"

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

#import "TAPProductListBubbleTableViewCell.h" //DV Temp
#import "TAPOrderCardBubbleTableViewCell.h" //CS Temp

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

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

@interface TAPChatViewController () <UIGestureRecognizerDelegate, UINavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource, TAPChatManagerDelegate, RNGrowingTextViewDelegate, TAPMyChatBubbleTableViewCellDelegate, TAPYourChatBubbleTableViewCellDelegate, TAPConnectionStatusViewControllerDelegate, TAPKeyboardViewControllerDelegate, UIImagePickerControllerDelegate>

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageTextViewHeightConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewHeightConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewLeftConstraint;
@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *tableViewTopConstraint;
@property (strong, nonatomic) IBOutlet UIButton *sendButton;
@property (strong, nonatomic) IBOutlet UIButton *keyboardOptionButton;
@property (strong, nonatomic) IBOutlet UIView *textViewBorderView;
@property (strong, nonatomic) IBOutlet RNGrowingTextView *messageTextView;
@property (strong, nonatomic) IBOutlet UITextField *secondaryTextField;
@property (strong, nonatomic) IBOutlet UIView *emptyView;
@property (strong, nonatomic) IBOutlet TAPCustomAccessoryView *inputMessageAccessoryView;
@property (strong, nonatomic) IBOutlet UILabel *emptyTitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *emptyDescriptionLabel;
@property (strong, nonatomic) IBOutlet RNImageView *senderImageView;
@property (strong, nonatomic) IBOutlet RNImageView *recipientImageView;

@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) TAPConnectionStatusViewController *connectionStatusViewController;
@property (strong, nonatomic) TAPKeyboardViewController *keyboardViewController;

@property (strong, nonatomic) NSMutableArray *messageArray;
@property (strong, nonatomic) NSMutableDictionary *messageDictionary;
@property (strong, nonatomic) TAPMessageModel *selectedMessage;
@property (strong, nonatomic) TAPOnlineStatusModel *onlineStatus;

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

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

@property (nonatomic) long apiBeforeLastCreated;
@property (nonatomic) BOOL isLastPage;
@property (nonatomic) BOOL isViewWillAppeared;
@property (nonatomic) BOOL isViewDidAppeared;
@property (nonatomic) KeyboardState keyboardState;
@property (nonatomic) BOOL isKeyboardWasShowed;
@property (nonatomic) BOOL isKeyboardShowed;
@property (nonatomic) BOOL isScrollViewDragged;
@property (nonatomic) BOOL isCustomKeyboardAvailable;

@property (nonatomic) CGFloat connectionStatusHeight;

@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) NSMutableArray *anchorUnreadMessageArray;
@property (strong, nonatomic) NSMutableArray *scrolledPendingMessageArray;

@property (nonatomic) BOOL isOnScrollPendingChecking;
@property (nonatomic) BOOL isNeedRefreshOnNetworkDown;

- (IBAction)sendButtonDidTapped:(id)sender;
- (IBAction)handleTapOnTableView:(UITapGestureRecognizer *)gestureRecognizer;
- (IBAction)chatAnchorButtonDidTapped:(id)sender;

- (void)backButtonDidTapped;
- (void)addIncomingMessageToArrayAndDictionaryWithMessage:(TAPMessageModel *)message atIndex:(NSInteger)index;
- (void)handleMessageFromSocket:(TAPMessageModel *)message;
- (void)destroySequence;
- (void)firstLoadData;
- (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated;
- (void)retrieveExistingMessages;
- (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray toTop:(BOOL)toTop;
- (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;
- (void)processVisibleMessageAsRead;
- (void)setAsTyping:(BOOL)typing;
- (NSString *)getOtherUserIDWithRoomID:(NSString *)roomID;
- (void)setAsTypingNoAfterDelay;

@end

@implementation TAPChatViewController

#pragma mark - Lifecycle
- (void)loadView {
    [super loadView];
    
    [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 - [TAPUtil safeAreaBottomPadding]);
    }];
    [UIView commitAnimations];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityStatusChange:) name:TAP_NOTIFICATION_REACHABILITY_STATUS_CHANGED object:nil];
    
    self.navigationController.delegate = self;
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
    
    if (IS_IPHONE_X_FAMILY) {
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding;
    }
    
    self.chatAnchorBadgeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.chatAnchorBadgeView.layer.colors = @[ (__bridge id)[TAPUtil getColor:@"9954C2"].CGColor, (__bridge id)[TAPUtil getColor:TAP_COLOR_MOSELO_PURPLE].CGColor];
    self.chatAnchorBadgeView.layer.borderColor = [TAPUtil getColor:TAP_COLOR_MOSELO_PURPLE].CGColor;
    self.chatAnchorBadgeView.layer.borderWidth = 1.0f;
    self.chatAnchorBadgeView.layer.cornerRadius = CGRectGetHeight(self.chatAnchorBadgeView.frame)/2.0f;
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:TAP_NOTIFICATION_APPLICATION_WILL_ENTER_FOREGROUND object:nil];
    
    _messageArray = [[NSMutableArray alloc] init];
    _messageDictionary = [[NSMutableDictionary alloc] init];
    _anchorUnreadMessageArray = [[NSMutableArray alloc] init];
    _scrolledPendingMessageArray = [[NSMutableArray alloc] init];
    
    [[TAPChatManager sharedManager] addDelegate:self];
    
    //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 = [TAPUtil getColor:TAP_COLOR_WHITE_F3];
    
    //TitleView
    _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, 6.0f, CGRectGetWidth(self.titleView.frame), 18.0f)];
    
    TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
    
    self.nameLabel.text = room.name;
    self.nameLabel.textColor = [TAPUtil getColor:TAP_COLOR_BLACK_44];
    self.nameLabel.font = [UIFont fontWithName:TAP_FONT_NAME_BOLD size:15.0f];
    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 = [TAPUtil getColor:@"19C700"];
    self.userStatusView.layer.cornerRadius = CGRectGetHeight(self.userStatusView.frame) / 2.0f;
    self.userStatusView.alpha = 0.0f;
    self.userStatusView.clipsToBounds = YES;
    
    _userStatusLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.userStatusView.frame) + 3.0f, 0.0f, 0.0f, 16.0f)];
    self.userStatusLabel.textColor = [TAPUtil getColor:TAP_COLOR_GREY_9B];
    self.userStatusLabel.font = [UIFont fontWithName:TAP_FONT_LATO_REGULAR size:13.0f];
    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) + 3.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];
    
    UILabel *typingLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, 100.0f, 16.0f)];
    typingLabel.text = NSLocalizedString(@"typing", @"");
    typingLabel.font = [UIFont fontWithName:TAP_FONT_LATO_REGULAR size:13.0f];
    typingLabel.textColor = [TAPUtil getColor:TAP_COLOR_GREY_9B];
    [typingLabel sizeToFit];
    typingLabel.frame = CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, CGRectGetWidth(typingLabel.frame), 16.0f);
    [self.userTypingView addSubview:typingLabel];
    
    self.userTypingView.frame = CGRectMake(CGRectGetMinX(self.userTypingView.frame), CGRectGetMinY(self.userTypingView.frame), CGRectGetMaxX(typingLabel.frame), CGRectGetHeight(self.userTypingView.frame));
    self.userTypingView.center = CGPointMake(self.nameLabel.center.x, self.userTypingView.center.y);
    
    [self setAsTyping:NO];
    [self isShowOnlineDotStatus:NO];
    
    //RightBarButton
    UIView *rightBarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 28.0f, 28.0f)];
    RNImageView *rightBarImageView = [[RNImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 28.0f, 28.0f)];
    [rightBarImageView setImageWithURLString:room.imageURL.thumbnail];
    
    rightBarImageView.layer.cornerRadius = CGRectGetHeight(rightBarImageView.frame) / 2.0f;
    rightBarImageView.clipsToBounds = YES;
    [rightBarView addSubview:rightBarImageView];
    
    UIImageView *expertIconImageView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(rightBarImageView.frame) - 13.0f, CGRectGetMaxY(rightBarImageView.frame) - 13.0f, 13.0f, 13.0f)];
    expertIconImageView.image = [UIImage imageNamed:@"TAPIconExpertSmall" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil];
    [rightBarView addSubview:expertIconImageView];
    
    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];
    [self showCustomBackButton];
    
    self.messageTextViewHeight = 32.0f;
    self.messageTextView.delegate = self;
    self.textViewBorderView.layer.cornerRadius = 18.0f;
    self.textViewBorderView.layer.borderColor = [TAPUtil getColor:@"E4E4E4"].CGColor;
    self.textViewBorderView.layer.borderWidth = 1.0f;
    self.textViewBorderView.clipsToBounds = YES;
    self.messageTextView.minimumHeight = 32.0f;
    self.messageTextView.maximumHeight = 64.0f;
    self.messageTextView.tintColor = [TAPUtil getColor:@"2ECCAD"];
    
    _safeAreaBottomPadding = [TAPUtil safeAreaBottomPadding];
    _selectedMessage = nil;
    
    _keyboardState = keyboardStateDefault;
    _keyboardHeight = 0.0f;
    _initialKeyboardHeight = 0.0f;
    
    //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]];
    self.keyboardViewController.delegate = self;
    
    TAPUserModel *currentUser = [TAPDataManager getActiveUser];
    TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:[self getOtherUserIDWithRoomID:self.currentRoom.roomID]];
    
    if([[[TAPCustomKeyboardManager sharedManager] getCustomKeyboardWithSender:currentUser recipient:currentUser] count] > 0) {
        //There's custom keyboard for this type
        
        NSArray *keyboardArray = [[TAPCustomKeyboardManager sharedManager] getCustomKeyboardWithSender:currentUser recipient:currentUser];
        
        [self.keyboardViewController setCustomKeyboardArray:keyboardArray sender:currentUser recipient:otherUser];
        _isCustomKeyboardAvailable = YES;
    }
    else {
        //There's no custom keyboard for this type
        _isCustomKeyboardAvailable = NO;
        self.keyboardOptionButton.alpha = 0.0f;
        self.messageViewLeftConstraint.constant = -38.0f;
    }
    //END Custom Keyboard
    
    _isKeyboardWasShowed = NO;
    _isKeyboardShowed = NO;
    
    _lastSeenTimer = [NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(timerRefreshLastSeen) userInfo:nil repeats:YES];
    
    [self firstLoadData];
    
    self.inputMessageAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    
    [[TAPChatManager sharedManager] refreshShouldRefreshOnlineStatus];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    _isViewWillAppeared = YES;
    
    self.connectionStatusViewController.view.frame = CGRectMake(CGRectGetMinX(self.connectionStatusViewController.view.frame), CGRectGetMinY(self.connectionStatusViewController.view.frame), CGRectGetWidth(self.connectionStatusViewController.view.frame), self.connectionStatusHeight);
    //    self.view.backgroundColor = [UIColor redColor];
    [self.tableView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
    //    [self checkEmptyState];
    
    //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([[TAPChatManager sharedManager] checkShouldRefreshOnlineStatus]) {
        [self setAsTyping:NO];
        [TAPDataManager callAPIGetUserByUserID:[self getOtherUserIDWithRoomID:self.currentRoom.roomID] success:^(TAPUserModel *user) {
            
            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];
            
        } failure:^(NSError *error) {
            
        }];
    }
}

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

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    _isViewWillAppeared = NO;
    
    if([self.delegate respondsToSelector:@selector(chatViewControllerShouldUpdateUnreadBubbleForRoomID:)]) {
        [self.delegate chatViewControllerShouldUpdateUnreadBubbleForRoomID:self.currentRoom.roomID];
    }
    
}

- (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)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];
}

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.messageArray count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    //DV Note - For product list height
    //    Collection view height (347.0f) + 16.0f gap
    //    return 363.0f;
    
    tableView.estimatedRowHeight = 70.0f;
    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) {
        TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row];
        
        //DV Note - For product list bubble
        //DV Temp
        //        [tableView registerNib:[TAPProductListBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPProductListBubbleTableViewCell description]];
        //        TAPProductListBubbleTableViewCell *cell = (TAPProductListBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPProductListBubbleTableViewCell description] forIndexPath:indexPath];
        //        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        //        return cell;
        //END DV Temp
        
        //CS Note - For order card bubble
        //CS Temp
        
        //        [tableView registerNib:[TAPOrderCardBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPOrderCardBubbleTableViewCell description]];
        //        TAPOrderCardBubbleTableViewCell *cell = (TAPOrderCardBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPOrderCardBubbleTableViewCell description] forIndexPath:indexPath];
        //        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        //        return cell;
        //END CS Temp
        
        if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) {
            //My Chat
            if (message.type == TAPChatMessageTypeText) {
                [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 setMessage:message];
                
                if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) {
                    [cell showStatusLabel:YES animated:NO updateStatusIcon:NO];
                }
                else {
                    [cell showStatusLabel:NO animated:NO updateStatusIcon:NO];
                }
                
                return cell;
            }
            else if (message.type == TAPChatMessageTypeImage) {
                [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 setMessage:message];
                
                return cell;
            }
            else if (message.type == TAPChatMessageTypeOrderCard) {
                [tableView registerNib:[TAPOrderCardBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPOrderCardBubbleTableViewCell description]];
                TAPOrderCardBubbleTableViewCell *cell = (TAPOrderCardBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPOrderCardBubbleTableViewCell description] forIndexPath:indexPath];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                [cell setOrderCardWithType:13];//CS Temp
                return cell;
            }
            else {
                //WK Temp - Where message.type is not 1001 or 1002, set empty chat 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;
                
                //WK Temp
                TAPMessageModel *editedMessage = message;
                editedMessage.body = @"";
                
                [cell setMessage:editedMessage];
                //End Temp
                
                if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) {
                    [cell showStatusLabel:YES animated:NO updateStatusIcon:NO];
                }
                else {
                    [cell showStatusLabel:NO animated:NO updateStatusIcon:NO];
                }
                
                return cell;
            }
        }
        else {
            //Their Chat
            
            //            //DV Temp
            //            NSInteger type = 3001;
            //            NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:type];
            //            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;
            //            return cell;
            //            //END DV Temp
            
            //            //DV Temp
            //            NSInteger type = 3002;
            //            NSString *cellName = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:type];
            //            UINib *cellNib = [UINib nibWithNibName:cellName bundle:[NSBundle mainBundle]];
            //            [tableView registerNib:cellNib forCellReuseIdentifier:cellName];
            //
            //            id cell = [tableView dequeueReusableCellWithIdentifier:cellName forIndexPath:indexPath];
            //
            //            return cell;
            //            //END DV Temp
            
            [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 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;
        }
        
    }
    
    [tableView registerNib:[TAPBaseXIBRotatedTableViewCell cellNib] forCellReuseIdentifier:[TAPBaseXIBRotatedTableViewCell description]];
    TAPBaseXIBRotatedTableViewCell *cell = (TAPBaseXIBRotatedTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPBaseXIBRotatedTableViewCell description] forIndexPath:indexPath];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    return cell;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.messageArray count] == 0) {
        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];
    }
    
    //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 retrieveExistingMessages];
    }
}

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

#pragma mark UIScrollView
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _isScrollViewDragged = YES;
}

- (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;
        
        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
            _keyboardState = keyboardStateDefault;
            [self.keyboardOptionButton setImage:[UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
        }
    }];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.contentOffset.y > kShowChatAnchorOffset) {
        if (self.chatAnchorButton.alpha != 1.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorButton.alpha = 1.0f;
            }];
            
            [self checkAnchorUnreadLabel];
        }
    }
    else {
        if (self.chatAnchorButton.alpha != 0.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                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;
    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;
        
        if (chatAnchorBottomConstraint < kChatAnchorDefaultBottomConstraint) {
            chatAnchorBottomConstraint = kChatAnchorDefaultBottomConstraint;
        }
        self.chatAnchorButtonBottomConstrait.constant = chatAnchorBottomConstraint;
    }
}

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

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

#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)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];
}

- (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];
}

- (void)chatManagerDidReceiveDeleteMessageInActiveRoom:(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];
}

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

- (void)chatManagerDidReceiveStartTyping:(TAPTypingModel *)typing {
    //    NSLog(@"USER %@ IS START TYPING", user.fullname); //DV Temp
    [self setAsTyping:YES];
}

- (void)chatManagerDidReceiveStopTyping:(TAPTypingModel *)typing {
    //    NSLog(@"USER %@ IS STOP TYPING", user.fullname); //DV Temp
    [self setAsTyping:NO];
}

#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];
                [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];
                    [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];
                    }
                    else {
                        [previousCell showStatusLabel:NO animated:YES];
                    }
                    [previousCell layoutIfNeeded];
                    
                    [cell showStatusLabel:YES animated:YES updateStatusIcon:YES];
                    [cell layoutIfNeeded];
                    [self.tableView beginUpdates];
                    [self.tableView endUpdates];
                } completion:^(BOOL finished) {
                    //completion
                }];
            }
        }
    }
}

- (void)myChatReplyDidTapped {
    //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.
    
    //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];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } completion:^(BOOL finished) {
        //completion
    }];
}

#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];
                    }
                    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 {
    //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.
    
    //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
    }];
}

#pragma mark RNGrowingTextView
- (void)growingTextView:(RNGrowingTextView *)textView shouldChangeHeight:(CGFloat)height {
    [UIView animateWithDuration:0.1f animations:^{
        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:(RNGrowingTextView *)textView {
    _keyboardState = keyboardStateDefault;
    
    [self.keyboardOptionButton setImage:[UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
    
    if (textView.text != nil) {
        if (![textView.text isEqualToString:@""]) {
            if(self.isCustomKeyboardAvailable) {
                [UIView animateWithDuration:0.2f animations:^{
                    self.keyboardOptionButton.alpha = 0.0f;
                    self.messageViewLeftConstraint.constant = -38.0f;
                    [self.messageTextView layoutIfNeeded];
                    [self.inputMessageAccessoryView layoutIfNeeded];
                    [self.view layoutIfNeeded];
                }];
            }
        }
    }
}

- (void)growingTextViewDidStartTyping:(RNGrowingTextView *)textView {
    [self.sendButton setImage:[UIImage imageNamed:@"TAPIconSendMessageActive" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
    self.sendButton.userInteractionEnabled = YES;
    if(self.isCustomKeyboardAvailable) {
        [UIView animateWithDuration:0.2f animations:^{
            self.keyboardOptionButton.alpha = 0.0f;
            self.messageViewLeftConstraint.constant = -38.0f;
            [self.messageTextView layoutIfNeeded];
            [self.inputMessageAccessoryView layoutIfNeeded];
        }];
    }
    [[TAPChatManager sharedManager] startTyping];
}

- (void)growingTextViewDidStopTyping:(RNGrowingTextView *)textView {
    [self.sendButton setImage:[UIImage imageNamed:@"TAPIconSendMessage" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
    self.sendButton.userInteractionEnabled = NO;
    if(self.isCustomKeyboardAvailable) {
        [UIView animateWithDuration:0.2f animations:^{
            self.keyboardOptionButton.alpha = 1.0f;
            self.messageViewLeftConstraint.constant = 4.0f;
            [self.messageTextView layoutIfNeeded];
            [self.inputMessageAccessoryView layoutIfNeeded];
            [self.view layoutIfNeeded];
        }];
    }
    [[TAPChatManager sharedManager] stopTyping];
}

#pragma mark TAPConnectionStatusViewController
- (void)connectionStatusViewControllerDelegateHeightChange:(CGFloat)height {
    self.connectionStatusHeight = height;
    [UIView animateWithDuration:0.2f animations:^{
        //change frame
        self.tableViewTopConstraint.constant = height - 50.0f;
        [self.view layoutIfNeeded];
    }];
}

#pragma mark TAPKeyboardViewController
- (void)keyboardViewControllerDidSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"===> %ld", indexPath.row); //WK Temp
    if(indexPath.row == 3) {
        //Create Order Card
        //CS Temp - Add Order Card Type Dummy Model
        TAPMessageModel *message = [TAPMessageModel createMessageWithUser:[TAPChatManager sharedManager].activeUser room:[TAPChatManager sharedManager].activeRoom body:@"" type:TAPChatMessageTypeOrderCard];
        [self.messageArray insertObject:message atIndex:0];
        [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
        //End Temp
    }
}

#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];
            }
            
            //CS Temp - Add Image Type Dummy Model
            TAPMessageModel *message = [TAPMessageModel createMessageWithUser:[TAPChatManager sharedManager].activeUser room:[TAPChatManager sharedManager].activeRoom body:@"" type:TAPChatMessageTypeImage];
            [RNImageView saveImageToCache:selectedImage withKey:message.localID];
            [self.messageArray insertObject:message atIndex:0];
            [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
            //End Temp
        }
        [self checkKeyboard];
    }];
}

#pragma mark - Custom Method
- (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)handleMessageFromSocket:(TAPMessageModel *)message {
    //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;
        
        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;
            }
            
            if(!currentMessage.isRead && message.isRead) {
                setAsDelivered = NO;
                setAsRead = YES;
            }
        }
        
        //Update message data
        [self updateMessageModelValueWithMessage:message];
        
        //Update view
        NSInteger indexInArray = [self.messageArray indexOfObject:currentMessage];
        NSIndexPath *messageIndexPath = [NSIndexPath indexPathForRow:indexInArray inSection:0];
        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 {
        //Message not exist in dictionary
        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 {
            //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)keyboardWillShowWithHeight:(CGFloat)keyboardHeight {
    
    CGFloat accessoryViewAndSafeAreaHeight = self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight;
    
    //set initial keyboard height to prevent wrong keyboard height usage
    if (self.initialKeyboardHeight == 0.0f && keyboardHeight != accessoryViewAndSafeAreaHeight) {
        self.initialKeyboardHeight = keyboardHeight;
    }
    
    if (self.keyboardHeight == 0.0f) {
        //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight
        if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) {
            _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;
            _keyboardHeight = keyboardHeight;
        }
    }
    
    //handle change keyboard height if keyboard is change to emoji
    if (keyboardHeight > self.initialKeyboardHeight && keyboardHeight != accessoryViewAndSafeAreaHeight) {
        _keyboardHeight = keyboardHeight;
    }
    
    //set keyboard height to initial height
    if (keyboardHeight == self.initialKeyboardHeight) {
        _keyboardHeight = self.initialKeyboardHeight;
    }
    
    if (self.isKeyboardShowed) {
        [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight];
    }
    
    //reject if scrollView is being dragged
    if (self.isScrollViewDragged) {
        return;
    }
    
    CGFloat tableViewYContentInset = 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);
    
    [UIView animateWithDuration:0.2f animations:^{
        self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight;
        CGFloat newYContentOffset = self.tableView.contentOffset.y - keyboardHeight + self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight;
        
        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) {
            _keyboardHeight = tempHeight;
        }
    }];
    
    if (keyboardHeight != accessoryViewAndSafeAreaHeight) {
        _isKeyboardShowed = YES;
    }
}

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

- (IBAction)sendButtonDidTapped:(id)sender {
    if ([self.messageArray count] != 0) {
        [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic
    }
    
    //Remove highlighted message.
    NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage];
    NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0];
    TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath];
    
    [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{
        //animation
        [cell showStatusLabel:NO animated:YES updateStatusIcon:YES];
        [cell layoutIfNeeded];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    } 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 {
        self.messageTextView.text = @"";
    }
    
    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];
}

- (IBAction)keyboardOptionButtonDidTapped:(id)sender {
    if (self.keyboardState == keyboardStateDefault) {
        _keyboardState = keyboardStateOptions;
        
        [self.keyboardOptionButton setImage:[UIImage imageNamed:@"TAPIconKeyboard" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
        [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 {
        _keyboardState = keyboardStateDefault;
        
        [self.keyboardOptionButton setImage:[UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
        if (IS_IPHONE_X_FAMILY) {
            if (self.isKeyboardShowed) {
                [UIView performWithoutAnimation:^{
                    [self.secondaryTextField resignFirstResponder];
                    [self.messageTextView becameFirstResponder];
                }];
            }
            else {
                [self.messageTextView becameFirstResponder];
            }
        }
        else {
            [self.messageTextView becameFirstResponder];
        }
    }
}

- (IBAction)attachmentButtonDidTapped:(id)sender {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    
    //    NSMutableAttributedString *documentsAttributedString = [[NSMutableAttributedString alloc] initWithString:@"Documents"];
    //    [documentsAttributedString addAttribute:NSFontAttributeName
    //                                      value:[UIFont fontWithName:TAP_FONT_LATO_REGULAR size:18.0f]
    //                                      range:NSMakeRange(0, documentsAttributedString.length)];
    
    UIAlertAction *documentsAction = [UIAlertAction
                                      actionWithTitle:@"Documents"
                                      style:UIAlertActionStyleDefault
                                      handler:^(UIAlertAction * action) {
                                          //Do some thing here
                                      }];
    
    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 *audioAction = [UIAlertAction
                                  actionWithTitle:@"Audio"
                                  style:UIAlertActionStyleDefault
                                  handler:^(UIAlertAction * action) {
                                      //Do some thing here
                                  }];
    
    UIAlertAction *locationAction = [UIAlertAction
                                     actionWithTitle:@"Location"
                                     style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction * action) {
                                         //Do some thing here
                                     }];
    
    UIAlertAction *contactAction = [UIAlertAction
                                    actionWithTitle:@"Contact"
                                    style:UIAlertActionStyleDefault
                                    handler:^(UIAlertAction * action) {
                                        //Do some thing here
                                    }];
    
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction * action) {
                                       //Do some thing here
                                       [self checkKeyboard];
                                   }];
    
    [documentsAction setValue:[[UIImage imageNamed:@"TAPIconDocuments" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [cameraAction setValue:[[UIImage imageNamed:@"TAPIconPhoto" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [galleryAction setValue:[[UIImage imageNamed:@"TAPIconGallery" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [audioAction setValue:[[UIImage imageNamed:@"TAPIconVoice" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [locationAction setValue:[[UIImage imageNamed:@"TAPIconLocation" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    [contactAction setValue:[[UIImage imageNamed:@"TAPIconContact" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];
    
    [documentsAction setValue:@0 forKey:@"titleTextAlignment"];
    [cameraAction setValue:@0 forKey:@"titleTextAlignment"];
    [galleryAction setValue:@0 forKey:@"titleTextAlignment"];
    [audioAction setValue:@0 forKey:@"titleTextAlignment"];
    [locationAction setValue:@0 forKey:@"titleTextAlignment"];
    [contactAction setValue:@0 forKey:@"titleTextAlignment"];
    
    [documentsAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [cameraAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [galleryAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [audioAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [locationAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [contactAction setValue:[TAPUtil getColor:TAP_COLOR_BLACK_2C] forKey:@"titleTextColor"];
    [cancelAction setValue:[TAPUtil getColor:TAP_COLOR_GREENBLUE_93] forKey:@"titleTextColor"];
    
    [alertController addAction:documentsAction];
    [alertController addAction:cameraAction];
    [alertController addAction:galleryAction];
    [alertController addAction:audioAction];
    [alertController addAction:locationAction];
    [alertController addAction:contactAction];
    [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)backButtonDidTapped {
    [self.lastSeenTimer invalidate];
    _lastSeenTimer = nil;
    [self destroySequence];
    
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)destroySequence {
    [self saveMessageDraft];
    [[TAPChatManager sharedManager] closeActiveRoom];
    
    //Remove ChatManager Delegate
    [[TAPChatManager sharedManager] removeDelegate:self];
}

- (void)checkEmptyState {
    if ([self.messageArray count] == 0) {
        if (self.emptyView.alpha == 1.0f) {
            return;
        }
        
        //show empty chat
        //WK Temp
        TAPUserModel *activeUser = [TAPDataManager getActiveUser];
        
        TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom;
        NSString *roomName = room.name;
        roomName = [TAPUtil nullToEmptyString:roomName];
        NSString *emptyTitleString = [NSString stringWithFormat:@"%@ is an expert\ndon’t forget to check out her services!", roomName];
        self.emptyTitleLabel.text = NSLocalizedString(emptyTitleString, @"");
        //set attributed string
        NSMutableDictionary *emptyTitleAttributesDictionary = [NSMutableDictionary dictionary];
        [emptyTitleAttributesDictionary setObject:[UIFont fontWithName:TAP_FONT_NAME_BOLD size:15.0f] forKey:NSFontAttributeName];
        [emptyTitleAttributesDictionary setObject:[TAPUtil getColor:TAP_COLOR_MOSELO_PURPLE] 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;
        }
        //End Temp
        
        self.emptyDescriptionLabel.text = @"Hey there! If you are looking for handmade gifts\nto give to someone special, please check out\nmy list of services and pricing below!";
        
        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 setImageWithURLString:activeUser.imageURL.thumbnail];
        self.senderImageView.backgroundColor = [UIColor clearColor];
        
        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 setImageWithURLString:room.imageURL.thumbnail];
        self.recipientImageView.backgroundColor = [UIColor clearColor];
        
        [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)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)firstLoadData {
    TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = roomData.roomID;
    
    NSDate *date = [NSDate date];
    NSTimeInterval createdDate = [date timeIntervalSince1970] * 1000.0f;
    
    [TAPDataManager getMessageWithRoomID:roomID lastMessageTimeStamp:[NSNumber numberWithDouble:createdDate] limitData:TAP_NUMBER_OF_ITEMS_CHAT success:^(NSArray<TAPMessageModel *> *messageArray) {
        if ([messageArray 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:messageArray toTop:NO];
            
            TAPMessageModel *earliestMessage = [messageArray objectAtIndex:[messageArray count] - 1];
            NSNumber *minCreated = earliestMessage.created;
            _minCreatedMessage = minCreated;
            
            TAPMessageModel *latestMessage = [messageArray 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
            [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:minCreated success:^(NSArray *messageArray) {
                
                //Update View
                [self updateMessageDataAndUIWithMessages:messageArray toTop:YES];
                
                //Update leftover message status to delivered
                if ([messageArray count] != 0) {
                    [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray];
                }
                
                //Call API Before Message if count < 50
                if ([messageArray count] < TAP_NUMBER_OF_ITEMS_CHAT) {
                    [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:roomID maxCreated:minCreated];
                }
                
            } failure:^(NSError *error) {
                //DV Temp
                UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Failed", @"") message:error.domain preferredStyle:UIAlertControllerStyleAlert];
                
                UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                }];
                
                [alertController addAction:okAction];
                [self presentViewController:alertController animated:YES completion:nil];
                //END DV Temp
            }];
        }
    } failure:^(NSError *error) {
        
    }];
}

- (void)retrieveExistingMessages {
    //Prevent retreive before message if already last page
    if (self.isLastPage) {
        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 *> *messageArray) {
        if ([messageArray count] > 0) {
            [self updateMessageDataAndUIWithMessages:messageArray toTop:NO];
        }
        
        //Call API Before when message array less than limit (50)
        if ([messageArray count] < TAP_NUMBER_OF_ITEMS_CHAT) {
            [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:lastMessage.room.roomID maxCreated:lastMessage.created];
        }
    } failure:^(NSError *error) {
        
    }];
}

- (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated {
    //Call API Get Before Message
    if ([self.loadedMaxCreated longLongValue] != [maxCreated longLongValue]) {
        _loadedMaxCreated = maxCreated;
        [TAPDataManager callAPIGetMessageBeforeWithRoomID:roomID maxCreated:maxCreated success:^(NSArray *messageArray, BOOL hasMore) {
            if ([messageArray count] != 0) {
                
                _isLastPage = !hasMore;
                
                //Update View
                [self updateMessageDataAndUIWithMessages:messageArray toTop:NO];
            }
        } failure:^(NSError *error) {
            //DV Temp
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Failed", @"") message:error.domain preferredStyle:UIAlertControllerStyleAlert];
            
            UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            }];
            
            [alertController addAction:okAction];
            [self presentViewController:alertController animated:YES completion:nil];
            //END DV Temp
        }];
    }
}

- (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray toTop:(BOOL)toTop {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        for (TAPMessageModel *message in messageArray) {
            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];
            }
        }
        
        [self sortAndFilterMessageArray];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
            
            if (toTop) {
                //RN To Do - Scroll to "Unread Message" marker after implemented
                [self.tableView scrollsToTop];
            }
            
            [self checkEmptyState];
        });
    });
}

- (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)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.isDeleted = message.isDeleted;
    currentMessage.isSending = message.isSending;
    currentMessage.isFailedSend = message.isFailedSend;
    
    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;
    }
    
    currentMessage.isHidden = message.isHidden;
}

- (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
    _keyboardState = keyboardStateDefault;
    [self.keyboardOptionButton setImage:[UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
    
    [self.messageTextView resignFirstResponder];
    [self.secondaryTextField resignFirstResponder];
}

- (void)callAPIAfterAndUpdateUIAndScrollToTop:(BOOL)scrollToTop {
    TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom;
    NSString *roomID = roomData.roomID;
    
    [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:self.minCreatedMessage success:^(NSArray *messageArray) {
        //Update View
        [self updateMessageDataAndUIWithMessages:messageArray toTop:scrollToTop];
        
        //Update leftover message status to delivered
        if ([messageArray count] != 0) {
            [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray];
        }
        
    } failure:^(NSError *error) {
        
    }];
}

- (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)applicationWillEnterForegroundNotification:(NSNotification *)notification {
    [self callAPIAfterAndUpdateUIAndScrollToTop:YES];
}

- (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.chatAnchorButton.alpha == 1.0f) {
            [UIView animateWithDuration:0.2f animations:^{
                self.chatAnchorBadgeView.alpha = 1.0f;
            }];
        }
        
        self.chatAnchorBadgeLabel.text = [NSString stringWithFormat:@"%li", [self.anchorUnreadMessageArray count]];
    }
}

- (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)profileImageDidTapped {
    TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init];
    profileViewController.room = [TAPChatManager sharedManager].activeRoom;
    [self.navigationController pushViewController:profileViewController animated:YES];
}

- (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
        }];
    }
    else if (status == PHAuthorizationStatusNotDetermined) {
        //request
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            [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_10_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_10_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)timerRefreshLastSeen {
    NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f;
    [self updateLastSeenWithTimestamp:currentLastSeen];
}

- (void)updateLastSeenWithTimestamp:(NSTimeInterval)timestamp {
    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(@"last seen 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(@"last seen %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(@"last seen %li %@ ago", @""), (long)numberOfHours, hourString];
        }
    }
    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", @"");
        
        if (timeGap <= 86400.0f) {
            dayString = NSLocalizedString(@"day", @"");
        }
        
        lastSeenString = [NSString stringWithFormat:NSLocalizedString(@"last seen %li %@ ago", @""), (long)numberOfDays, dayString];
    }
    else if (timeGap <= 86400.0f*7 + midnightTimeGap) {
        //Set a week ago
        lastSeenString = @"last seen a week ago";
    }
    else {
        //Set date
        NSDate *lastLoginDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
        
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        dateFormatter.dateFormat = @"dd/MM/YY";
        NSString *formattedCreatedDate = [dateFormatter stringFromDate:lastLoginDate];
        
        lastSeenString = [NSString stringWithFormat:@"last seen %@", 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) + 3.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) + 3.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);
    }
}

//Implement Input Accessory View
- (UIView *)inputAccessoryView {
    return self.inputMessageAccessoryView;
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)processMessageAsRead:(TAPMessageModel *)message {
    BOOL isRead = message.isRead;
    
    if(!self.isViewDidAppeared) {
        //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
    NSLog(@"READ MESSAGE: %@", message.body);
    
    message.isRead = YES;
    
    //Call Message Status Manager mark as read call API
    [[TAPMessageStatusManager sharedManager] markMessageAsReadWithMessage:message];
    
    //Call Notification Manager remove local notification
    [[TAPNotificationManager sharedManager] removeReadLocalNotificationWithMessage:message];
}

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

- (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;
    }
}

- (void)setAsTyping:(BOOL)typing {
    if(typing) {
        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;
    }
}

- (NSString *)getOtherUserIDWithRoomID:(NSString *)roomID {
    NSArray *roomIDArray = [roomID componentsSeparatedByString:@"-"];
    if([roomIDArray count] > 0) {
        for (NSString *userID in roomIDArray) {
            if(![userID isEqualToString:[TAPDataManager getActiveUser].userID]) {
                return userID;
            }
        }
    }
    
    return @"";
}

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

@end