/*
 Copyright 2015 OpenMarket Ltd
 Copyright 2017 Vector Creations Ltd
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import "MXKInterleavedRecentsDataSource.h"

#import "MXKInterleavedRecentTableViewCell.h"

#import "MXKAccountManager.h"

#import "NSBundle+MatrixKit.h"

@interface MXKInterleavedRecentsDataSource ()
{
    /**
     The interleaved recents: cell data served by `MXKInterleavedRecentsDataSource`.
     */
    NSMutableArray *interleavedCellDataArray;
}

@end

@implementation MXKInterleavedRecentsDataSource

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        interleavedCellDataArray = [NSMutableArray array];
    }
    return self;
}

#pragma mark - Override MXKDataSource

- (void)destroy
{
    interleavedCellDataArray = nil;
    
    [super destroy];
}

#pragma mark - Override MXKRecentsDataSource

- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView
{
    UIView *sectionHeader = nil;
    
    if (displayedRecentsDataSourceArray.count > 1 && section == 0)
    {
        sectionHeader = [[UIView alloc] initWithFrame:frame];
        sectionHeader.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];
        CGFloat btnWidth = frame.size.width / displayedRecentsDataSourceArray.count;
        UIButton *previousShrinkButton;
        
        for (NSInteger index = 0; index < displayedRecentsDataSourceArray.count; index++)
        {
            MXKSessionRecentsDataSource *recentsDataSource = [displayedRecentsDataSourceArray objectAtIndex:index];
            NSString* btnTitle = recentsDataSource.mxSession.myUser.userId;
            
            // Add shrink button
            UIButton *shrinkButton = [UIButton buttonWithType:UIButtonTypeCustom];
            CGRect btnFrame = CGRectMake(index * btnWidth, 0, btnWidth, sectionHeader.frame.size.height);
            shrinkButton.frame = btnFrame;
            shrinkButton.backgroundColor = [UIColor clearColor];
            
            [shrinkButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
            shrinkButton.tag = index;
            [sectionHeader addSubview:shrinkButton];
            sectionHeader.userInteractionEnabled = YES;
            
            // Set shrink button constraints
            NSLayoutConstraint *leftConstraint;
            NSLayoutConstraint *widthConstraint;
            shrinkButton.translatesAutoresizingMaskIntoConstraints = NO;
            if (!previousShrinkButton)
            {
                leftConstraint = [NSLayoutConstraint constraintWithItem:shrinkButton
                                                              attribute:NSLayoutAttributeLeading
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:sectionHeader
                                                              attribute:NSLayoutAttributeLeading
                                                             multiplier:1
                                                               constant:0];
                widthConstraint = [NSLayoutConstraint constraintWithItem:shrinkButton
                                                               attribute:NSLayoutAttributeWidth
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:sectionHeader
                                                               attribute:NSLayoutAttributeWidth
                                                              multiplier:(1.0 /displayedRecentsDataSourceArray.count)
                                                                constant:0];
            }
            else
            {
                leftConstraint = [NSLayoutConstraint constraintWithItem:shrinkButton
                                                              attribute:NSLayoutAttributeLeading
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:previousShrinkButton
                                                              attribute:NSLayoutAttributeTrailing
                                                             multiplier:1
                                                               constant:0];
                widthConstraint = [NSLayoutConstraint constraintWithItem:shrinkButton
                                                               attribute:NSLayoutAttributeWidth
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:previousShrinkButton
                                                               attribute:NSLayoutAttributeWidth
                                                              multiplier:1
                                                                constant:0];
            }
            [NSLayoutConstraint activateConstraints:@[leftConstraint, widthConstraint]];
            previousShrinkButton = shrinkButton;
            
            // Add shrink icon
            UIImage *chevron;
            if ([shrinkedRecentsDataSourceArray indexOfObject:recentsDataSource] != NSNotFound)
            {
                chevron = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"disclosure"];
            }
            else
            {
                chevron = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"shrink"];
            }
            UIImageView *chevronView = [[UIImageView alloc] initWithImage:chevron];
            if ([shrinkedRecentsDataSourceArray indexOfObject:recentsDataSource] == NSNotFound)
            {
                // Display the tint color of the user
                MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:recentsDataSource.mxSession.myUser.userId];
                if (account)
                {
                    chevronView.backgroundColor = account.userTintColor;
                }
                else
                {
                    chevronView.backgroundColor = [UIColor clearColor];
                }
            }
            else
            {
                chevronView.backgroundColor = [UIColor lightGrayColor];
            }
            chevronView.contentMode = UIViewContentModeCenter;
            frame = chevronView.frame;
            frame.size.width = frame.size.height = shrinkButton.frame.size.height - 10;
            frame.origin.x = shrinkButton.frame.size.width - frame.size.width - 8;
            frame.origin.y = (shrinkButton.frame.size.height - frame.size.height) / 2;
            chevronView.frame = frame;
            [shrinkButton addSubview:chevronView];
            chevronView.autoresizingMask |= (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);
            
            // Add label
            frame = shrinkButton.frame;
            frame.origin.x = 5;
            frame.origin.y = 5;
            frame.size.width = chevronView.frame.origin.x - 10;
            frame.size.height -= 10;
            UILabel *headerLabel = [[UILabel alloc] initWithFrame:frame];
            headerLabel.font = [UIFont boldSystemFontOfSize:16];
            headerLabel.backgroundColor = [UIColor clearColor];
            headerLabel.text = btnTitle;
            [shrinkButton addSubview:headerLabel];
            headerLabel.autoresizingMask |= (UIViewAutoresizingFlexibleWidth);
        }
    }
    
    return sectionHeader;
}

- (id<MXKRecentCellDataStoring>)cellDataAtIndexPath:(NSIndexPath *)indexPath
{
    id<MXKRecentCellDataStoring> cellData = nil;
    
    // Only one section is handled by this data source
    if (indexPath.section == 0)
    {
        // Consider first the case where there is only one data source (no interleaving).
        if (displayedRecentsDataSourceArray.count == 1)
        {
            MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
            cellData = [recentsDataSource cellDataAtIndex:indexPath.row];
        }
        // Else all the cells have been interleaved.
        else if (indexPath.row < interleavedCellDataArray.count)
        {
            cellData = interleavedCellDataArray[indexPath.row];
        }
    }
    
    return cellData;
}

- (CGFloat)cellHeightAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = 0;
    
    // Only one section is handled by this data source
    if (indexPath.section == 0)
    {
        // Consider first the case where there is only one data source (no interleaving).
        if (displayedRecentsDataSourceArray.count == 1)
        {
            MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
            height = [recentsDataSource cellHeightAtIndex:indexPath.row];
        }
        // Else all the cells have been interleaved.
        else if (indexPath.row < interleavedCellDataArray.count)
        {
            id<MXKRecentCellDataStoring> recentCellData = interleavedCellDataArray[indexPath.row];
            
            // Select the related recent data source
            MXKDataSource *dataSource = recentCellData.dataSource;
            if ([dataSource isKindOfClass:[MXKSessionRecentsDataSource class]])
            {
                MXKSessionRecentsDataSource *recentsDataSource = (MXKSessionRecentsDataSource*)dataSource;
                // Count the index of this cell data in original data source array
                NSInteger rank = 0;
                for (NSInteger index = 0; index < indexPath.row; index++)
                {
                    id<MXKRecentCellDataStoring> cellData = interleavedCellDataArray[index];
                    if (cellData.roomSummary == recentCellData.roomSummary)
                    {
                        rank++;
                    }
                }
                
                height = [recentsDataSource cellHeightAtIndex:rank];
            }
        }
    }
    
    return height;
}

- (NSIndexPath*)cellIndexPathWithRoomId:(NSString*)roomId andMatrixSession:(MXSession*)matrixSession
{
    NSIndexPath *indexPath = nil;
    
    // Consider first the case where there is only one data source (no interleaving).
    if (displayedRecentsDataSourceArray.count == 1)
    {
        MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
        if (recentsDataSource.mxSession == matrixSession)
        {
            // Look for the cell
            for (NSInteger index = 0; index < recentsDataSource.numberOfCells; index ++)
            {
                id<MXKRecentCellDataStoring> recentCellData = [recentsDataSource cellDataAtIndex:index];
                if ([roomId isEqualToString:recentCellData.roomIdentifier])
                {
                    // Got it
                    indexPath = [NSIndexPath indexPathForRow:index inSection:0];
                    break;
                }
            }
        }
    }
    else
    {
        // Look for the right data source
        for (MXKSessionRecentsDataSource *recentsDataSource in displayedRecentsDataSourceArray)
        {
            if (recentsDataSource.mxSession == matrixSession)
            {
                // Check whether the source is not shrinked
                if ([shrinkedRecentsDataSourceArray indexOfObject:recentsDataSource] == NSNotFound)
                {
                    // Look for the cell
                    for (NSInteger index = 0; index < interleavedCellDataArray.count; index ++)
                    {
                        id<MXKRecentCellDataStoring> recentCellData = interleavedCellDataArray[index];
                        if ([roomId isEqualToString:recentCellData.roomIdentifier])
                        {
                            // Got it
                            indexPath = [NSIndexPath indexPathForRow:index inSection:0];
                            break;
                        }
                    }
                }
                break;
            }
        }
    }
    
    return indexPath;
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Check whether all data sources are ready before rendering recents
    if (self.state == MXKDataSourceStateReady)
    {
        // Only one section is handled by this data source.
        return (displayedRecentsDataSourceArray.count ? 1 : 0);
    }
    return 0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Consider first the case where there is only one data source (no interleaving).
    if (displayedRecentsDataSourceArray.count == 1)
    {
        MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
        return recentsDataSource.numberOfCells;
    }
    
    return interleavedCellDataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    id<MXKRecentCellDataStoring> roomData = [self cellDataAtIndexPath:indexPath];
    if (roomData && self.delegate)
    {
        NSString *cellIdentifier = [self.delegate cellReuseIdentifierForCellData:roomData];
        if (cellIdentifier)
        {
            UITableViewCell<MXKCellRendering> *cell  = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
            
            // Make sure we listen to user actions on the cell
            cell.delegate = self;
            
            // Make the bubble display the data
            [cell render:roomData];
            
            // Clear the user flag, if only one recents list is available
            if (displayedRecentsDataSourceArray.count == 1 && [cell isKindOfClass:[MXKInterleavedRecentTableViewCell class]])
            {
                ((MXKInterleavedRecentTableViewCell*)cell).userFlag.backgroundColor = [UIColor clearColor];
            }
            
            return cell;
        }
    }
    
    // Return a fake cell to prevent app from crashing.
    return [[UITableViewCell alloc] init];
}

#pragma mark - MXKDataSourceDelegate

- (void)dataSource:(MXKDataSource*)dataSource didCellChange:(id)changes
{
    // Consider first the case where there is only one data source (no interleaving).
    if (displayedRecentsDataSourceArray.count == 1)
    {
        // Flush interleaved cells array, we will refer directly to the cell data of the unique data source.
        [interleavedCellDataArray removeAllObjects];
    }
    else
    {
        // Handle here the specific case where a second source is just added.
        // The empty interleaved cells array has to be prefilled with the cell data of the other source (except if this other source is shrinked).
        if (!interleavedCellDataArray.count && displayedRecentsDataSourceArray.count == 2)
        {
            // This is the first interleaving, look for the other source
            MXKSessionRecentsDataSource *recentsDataSource = displayedRecentsDataSourceArray.firstObject;
            if (recentsDataSource == dataSource)
            {
                recentsDataSource = displayedRecentsDataSourceArray.lastObject;
            }
            
            if ([shrinkedRecentsDataSourceArray indexOfObject:recentsDataSource] == NSNotFound)
            {
                // Report all cell data
                for (NSInteger index = 0; index < recentsDataSource.numberOfCells; index ++)
                {
                    [interleavedCellDataArray addObject:[recentsDataSource cellDataAtIndex:index]];
                }
            }
        }
        
        // Update now interleaved cells array, TODO take into account 'changes' parameter
        MXKSessionRecentsDataSource *updateRecentsDataSource = (MXKSessionRecentsDataSource*)dataSource;
        NSInteger numberOfUpdatedCells = 0;
        // Check whether this dataSource is used
        if ([displayedRecentsDataSourceArray indexOfObject:dataSource] != NSNotFound && [shrinkedRecentsDataSourceArray indexOfObject:dataSource] == NSNotFound)
        {
            numberOfUpdatedCells = updateRecentsDataSource.numberOfCells;
        }
        
        NSInteger currentCellIndex = 0;
        NSInteger updatedCellIndex = 0;
        id<MXKRecentCellDataStoring> updatedCellData = nil;
        
        if (numberOfUpdatedCells)
        {
            updatedCellData = [updateRecentsDataSource cellDataAtIndex:updatedCellIndex++];
        }
        
        // Review all cell data items of the current list
        while (currentCellIndex < interleavedCellDataArray.count)
        {
            id<MXKRecentCellDataStoring> currentCellData = interleavedCellDataArray[currentCellIndex];
            
            // Remove existing cell data of the updated data source
            if (currentCellData.dataSource == dataSource)
            {
                [interleavedCellDataArray removeObjectAtIndex:currentCellIndex];
            }
            else
            {
                while (updatedCellData && (updatedCellData.roomSummary.lastMessage.originServerTs > currentCellData.roomSummary.lastMessage.originServerTs))
                {
                    [interleavedCellDataArray insertObject:updatedCellData atIndex:currentCellIndex++];
                    updatedCellData = [updateRecentsDataSource cellDataAtIndex:updatedCellIndex++];
                }
                
                currentCellIndex++;
            }
        }
        
        while (updatedCellData)
        {
            [interleavedCellDataArray addObject:updatedCellData];
            updatedCellData = [updateRecentsDataSource cellDataAtIndex:updatedCellIndex++];
        }
    }
    
    // Call super to keep update readyRecentsDataSourceArray.
    [super dataSource:dataSource didCellChange:changes];
}

@end
