//
// SearchViewController.m
// IvyMedia
//
// Created by haibara on 8/26/14.
//
//

#import "ComboBoxViewController.h"
#import "Lang.h"
#import "SDK.h"
#import "BeanUtils.h"
#import "CocoaUtils.h"
#import "DrawUtils.h"
#import "UIToolkit.h"
#import "DZNEmptyDataSetDefault.h"
#import <BlocksKit/BlocksKit.h>
#import <BlocksKit/BlocksKit+UIKit.h>
#import <libextobjc/EXTScope.h>
#import <PINCache/PINCache.h>

@implementation ComboBoxItem

- (BOOL)isEqual:(id)object {
	if (self == object) {
		return YES;
	}
	if (![object isKindOfClass:self.class]) {
		return NO;
	}
    return [[BeanUtils describe:self] isEqualToDictionary:[BeanUtils describe:object]];
}

@end

@interface ComboBoxViewController () <UITableViewDataSource, UITableViewDelegate, UISearchControllerDelegate, UISearchResultsUpdating, DZNEmptyDataSetDefault> {
	CGPoint _restorationContentOffset;
}

@property (nonatomic, weak) IBOutlet UITableView *tableView;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *topConstraint;
@property (nonatomic, strong) UISearchController *searchController;

@property (nonatomic, strong) NSArray *allItems;
@property (nonatomic, strong) NSMutableArray *selectedItems;
@property (nonatomic, strong) NSArray *suggestedItems;
@property (nonatomic, strong) NSMutableArray *historyItems;

@property (nonatomic, strong) NSMutableArray *selectionCellDataArray;
@property (nonatomic, strong) NSArray *locationCellDataArray;
@property (nonatomic, strong) NSMutableArray *historyCellDataArray;
@property(nonatomic, strong)  NSArray *cellDataArray;
@property (nonatomic, strong) NSArray *suggestionCellDataArray;
@property (nonatomic, strong) NSArray *collationCellDataArrays;
@property (nonatomic, strong) NSArray *searchCellDataArray;
@property (nonatomic, strong) NSArray *indexTitles;
@property (nonatomic, strong) NSArray *sortedArrays;
@property (nonatomic, assign) BOOL isAllChanged;
@property (nonatomic, assign) BOOL isSelectionChanged;
@property (nonatomic, assign) BOOL isSuggestionChanged;
@property (nonatomic, assign) BOOL isHistoryChanged;
@property (nonatomic, assign) BOOL geocoding;

@property (nonatomic, strong, readwrite) ComboBoxItem *selectedItem;
@property (nonatomic, strong, readwrite) CLPlacemark *currentPlacemark;
@property (nonatomic, assign, readonly) BOOL searching;

@property (nonatomic, copy) void (^didDoneBlock) (id obj);
@property (nonatomic, copy) void (^didCancelBlock) (void);
@property (nonatomic, copy) void (^didSelectItemBlock) (ComboBoxItem *item);
@property (nonatomic, copy) void (^selectionChangedBlock) (ComboBoxViewController *viewController, id obj);
@property (nonatomic, copy) void (^historyChangedBlock)(NSArray<ComboBoxItem *> *items);
@property (nonatomic, copy) void (^geocodeBlock) (ComboBoxViewController *viewController);
@property (nonatomic, copy) NSString *(^placemarkFormattingBlock) (CLPlacemark *placemark);
@property (nonatomic, copy) id (^placemarkTransformationBlock) (CLPlacemark *placemark);

@end

@implementation ComboBoxViewController

- (instancetype)init {
	self = [super init];
	if (self) {
		self.edgesForExtendedLayout = UIRectEdgeNone;

		self.allowsIndexedCollation = YES;
		self.selectionTitle = IMLocalizedString(@"Selected");
		self.locationTitle = IMLocalizedString(@"My Location");

		self.allItems = @[];
		self.selectedItems = [NSMutableArray array];
		self.historyItems = [NSMutableArray array];
		self.suggestedItems = @[];
		self.cellDataArray = @[];
	}
	return self;
}

- (void)viewDidLoad {
	[super viewDidLoad];

	@weakify(self)
	if (self.didCancelBlock) {
		self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] im_initWithBarButtonSystemItem:UIBarButtonSystemItemCancel handler:^(UIBarButtonItem *barButtonItem) {
			@strongify(self)
			self.didCancelBlock();
		}];
	}
	if (self.didDoneBlock) {
		if (self.allowsMultipleSelection) {
			self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] im_initWithBarButtonSystemItem:UIBarButtonSystemItemDone handler:^(UIBarButtonItem *barButtonItem) {
				@strongify(self)
				self.didDoneBlock([NSArray arrayWithArray:self.selectedItems]);
			}];
		} else {
			self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] im_initWithBarButtonSystemItem:UIBarButtonSystemItemDone handler:^(UIBarButtonItem *barButtonItem) {
				@strongify(self)
				self.didDoneBlock(self.selectedItem);
			}];
		}
	}

	self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
	self.searchController.delegate = self;
	self.searchController.searchResultsUpdater = self;
	if ([self.searchController respondsToSelector:@selector(setObscuresBackgroundDuringPresentation:)]) {
		self.searchController.obscuresBackgroundDuringPresentation = NO;
	} else {
		self.searchController.dimsBackgroundDuringPresentation = NO;
	}
    self.searchController.hidesNavigationBarDuringPresentation = NO;
	if ([self.searchController respondsToSelector:@selector(loadViewIfNeeded)]) {
		[self.searchController loadViewIfNeeded];
	}

	[self.searchController.searchBar sizeToFit];
	[self.view addSubview:self.searchController.searchBar];

    self.topConstraint.constant = CGRectGetHeight(self.searchController.searchBar.frame);

	self.tableView.emptyDataSetSource = self;
	if (self.allowsIndexedCollation) {
		[self makeIndexedCollation];
	}
	[self buildCellDataSource];
	[self.tableView reloadData];
}

- (void)willMoveToParentViewController:(UIViewController *)parent {
	if (self.searchController.active) {
		self.searchController.active = NO;
	}
}

- (void)viewDidAppear:(BOOL)animated {
	[super viewDidAppear:animated];

	if (self.allowsLocation && self.geocodeBlock) {
		static dispatch_once_t onceToken;
		dispatch_once(&onceToken, ^{
			[self didStartGeocode];
		});
	}
}

#pragma mark - Public

- (void)setItemsWithDictionary:(NSDictionary *)dictionary {
	NSMutableArray *array1 = [NSMutableArray array];
	for (id key in dictionary.allKeys) {
		ComboBoxItem *item = [[ComboBoxItem alloc] init];
		item.label = dictionary[key];
		item.value = key;
		[array1 addObject:item];
	}
	self.allItems = [NSArray arrayWithArray:array1];
	self.isAllChanged = YES;
}

- (void)setItemsWithArray:(NSArray *)array {
	[self setItemsWithDictionary:[NSDictionary dictionaryWithObjects:array forKeys:array]];
}

- (void)selectValue:(id)value {
	[self selectValues:@[value]];
}

- (void)selectValues:(NSArray *)values {
	self.selectedItems = [self matchedItemsWithValues:values];
	self.isSelectionChanged = YES;
}

- (void)suggestValues:(NSArray *)values {
	self.suggestedItems = [NSArray arrayWithArray:[self matchedItemsWithValues:values]];
	self.isSuggestionChanged = YES;
}

- (void)setHistoryValues:(NSArray *)values {
	self.historyItems = [self matchedItemsWithValues:values];
	self.isHistoryChanged = YES;
}

- (void)reloadIfNeeded {
	NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
	if (self.isSelectionChanged || self.isAllChanged) {
		[self updateTableViewCellDataArrayForSelection];
		[indexSet addIndex:0];
	}
	if (self.isHistoryChanged || self.isAllChanged) {
		[self updateTableViewCellDataArrayForHistory];
		[indexSet addIndex:2];
	}
	if (self.isSuggestionChanged || self.isAllChanged) {
		[self updateTableViewCellDataArrayForSuggestion];
		[indexSet addIndex:3];
	}
	if (self.allowsIndexedCollation && self.isAllChanged) {
		[self makeIndexedCollation];
		[self updateTableViewCellDataArrayForIndexedCollation];
	}
	if (self.isAllChanged) {
		[self.tableView reloadData];
	} else if (indexSet.count > 0) {
		[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
	}
	self.isAllChanged = NO;
	self.isSelectionChanged = NO;
	self.isHistoryChanged = NO;
	self.isSuggestionChanged = NO;

}

- (void)updatePlacemark:(CLPlacemark *)placemark {
	self.currentPlacemark = placemark;
	[self didStopGeocode];
}


#pragma mark - Property

- (BOOL)allowsDeselection {
	if (self.allowsMultipleSelection) {
		return YES;
	}
	return _allowsDeselection;
}

- (ComboBoxItem *)selectedItem {
	if (self.allowsMultipleSelection) {
		return nil;
	}
	return self.selectedItems.firstObject;
}

- (void)setSelectedItem:(ComboBoxItem *)selectedItem {
	if (self.allowsMultipleSelection) {
		return;
	}
	[self.selectedItems removeAllObjects];
	[self.selectedItems addObject:selectedItem];
}

- (CLPlacemark *)currentPlacemark {
	return [[PINCache sharedCache] objectForKey:[NSString stringWithFormat:@"%@-%@", NSStringFromClass(self.class), NSStringFromSelector(@selector(currentPlacemark))]];
}

- (void)setCurrentPlacemark:(CLPlacemark *)currentPlacemark {
	if (currentPlacemark) {
		[[PINCache sharedCache] setObject:currentPlacemark forKey:[NSString stringWithFormat:@"%@-%@", NSStringFromClass(self.class), NSStringFromSelector(@selector(currentPlacemark))]];
	}
}

- (BOOL)searching {
	return self.searchController.active && ![StringUtils isEmpty:self.searchController.searchBar.text];
}

#pragma mark - Action

- (void)didDeselectSelectionItem:(ComboBoxItem *)item {
	[self deselectItem:item onTableView:self.tableView];
}

- (void)didSelectLocationItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	[self selectItem:item onTableView:tableView];
}

- (void)didSelectHistoryItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	[self selectItem:item onTableView:tableView];
}

- (void)didSelectSuggestionItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	[self selectItem:item onTableView:tableView];
}

- (void)didSelectCollationItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	[self selectItem:item onTableView:tableView];
}

- (void)didSelectSearchItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	self.searchController.active = NO;
	[self selectItem:item onTableView:tableView];
}

- (void)didStartGeocode {
	self.geocoding = YES;
	if (!self.searching) {
		[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone];
	}
	self.geocodeBlock(self);
}

#pragma mark - Private

- (NSMutableArray *)matchedItemsWithValues:(NSArray *)values {
	NSMutableArray *array = [NSMutableArray array];
	[values enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
		ComboBoxItem *matched = [self.allItems bk_match:^BOOL (ComboBoxItem *item) {
			return [[obj description] isEqualToString:[item.value description]];
		}];
		if (matched) {
			[array addObject:matched];
		}
	}];
	return array;
}

- (void)didStopGeocode {
	self.geocoding = NO;
	if (!self.searching) {
		[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone];
	}
}

- (void)selectItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if (self.searchController.active) {
		self.searchController.active = NO;
	}
	if (self.allowsMultipleSelection) {
		[self insertSelectionItem:item onTableView:tableView];
	} else {
		[self reloadSelectionItem:item onTableView:tableView];
	}
	if (self.didSelectItemBlock) {
		self.didSelectItemBlock(item);
	}
	if (self.allowsMultipleSelection) {
		if (self.selectionChangedBlock) {
			self.selectionChangedBlock(self, [NSArray arrayWithArray:self.selectedItems]);
		}
	} else {
		if (self.selectionChangedBlock) {
			self.selectionChangedBlock(self, self.selectedItem);
		}
	}
}

- (void)deselectItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if (self.searchController.active) {
		self.searchController.active = NO;
	}
	if (self.allowsDeselection) {
		[self deleteSelectionItem:item onTableView:tableView];
	}
	if (self.allowsMultipleSelection) {
		if (self.selectionChangedBlock) {
			self.selectionChangedBlock(self, [NSArray arrayWithArray:self.selectedItems]);
		}
	} else {
		if (self.selectionChangedBlock) {
			self.selectionChangedBlock(self, self.selectedItem);
		}
	}
}

- (void)deleteSelectionItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if (![self.selectedItems containsObject:item]) {
		return;
	}
	NSInteger row = [self.selectedItems indexOfObject:item];
	[self.selectedItems removeObject:item];
	NSMutableArray *array = self.selectionCellDataArray;
	[array removeObjectAtIndex:row];
	[tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
}

- (void)insertSelectionItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if ([self.selectedItems containsObject:item]) {
		return;
	}
	NSInteger row = self.selectedItems.count;
	[self.selectedItems addObject:item];
	NSMutableArray *array = self.selectionCellDataArray;
	[array addObject:[self convertTableViewCellDataFromSelectionItem:item]];
	[tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
}

- (void)deleteHistoryItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if (![self.historyItems containsObject:item]) {
		return;
	}
	NSInteger row = [self.historyItems indexOfObject:item];
	[self.historyItems removeObject:item];
	NSMutableArray *array = self.historyCellDataArray;
	[array removeObjectAtIndex:row];
	[tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:2]] withRowAnimation:UITableViewRowAnimationNone];
	if (self.historyChangedBlock) {
		self.historyChangedBlock([NSArray arrayWithArray:self.historyItems]);
	}
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
}

- (void)reloadSelectionItem:(ComboBoxItem *)item onTableView:(UITableView *)tableView {
	if ([self.selectedItems containsObject:self.selectedItem]) {
		NSInteger row = [self.selectedItems indexOfObject:self.selectedItem];
		[self.selectedItems replaceObjectAtIndex:row withObject:item];
		NSMutableArray *array = self.selectionCellDataArray;
		[array replaceObjectAtIndex:row withObject:[self convertTableViewCellDataFromSelectionItem:item]];
		[tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
	} else {
		[self insertSelectionItem:item onTableView:tableView];
	}
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
}

- (void)buildCellDataSource {
	self.selectionCellDataArray = [NSMutableArray array];
	[self updateTableViewCellDataArrayForSelection];
	[self updateTableViewCellDataArrayForLocation];
	[self updateTableViewCellDataArrayForHistory];
	[self updateTableViewCellDataArrayForSuggestion];
	[self updateTableViewCellDataArrayForIndexedCollation];
	self.searchCellDataArray = @[];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
}

- (TableViewCellData *)convertTableViewCellDataFromSelectionItem:(ComboBoxItem *)item {
	TableViewCellData *data = [[TableViewCellData alloc] init];
	data.text = item.label;
	data.allowsTextLineWrapping = YES;
	if (self.allowsDeselection) {
		@weakify(self)
		[data setAccessoryViewBlock:^UIView *(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
			[button setAttributedTitle:[DrawUtils attributedStringWithFAKIcon:[FAKIonIcons iosCloseOutlineIconWithSize:24] fillColor:[UIColor darkGrayColor]] forState:UIControlStateNormal];
			[button sizeToFit];
			[button bk_addEventHandler:^(id sender) {
				@strongify(self)
				[self didDeselectSelectionItem: item];
			} forControlEvents:UIControlEventTouchUpInside];
			return button;
		}];
	}
	return data;
}

- (void)updateTableViewCellDataArrayForSelection {
	NSMutableArray *array = self.selectionCellDataArray;
	[array removeAllObjects];
	for (ComboBoxItem *item in self.selectedItems) {
		[array addObject:[self convertTableViewCellDataFromSelectionItem:item]];
	}
}

- (void)updateTableViewCellDataArrayForLocation {
	if (!self.allowsLocation || !self.geocodeBlock || !self.placemarkFormattingBlock || !self.placemarkTransformationBlock) {
		self.locationCellDataArray = @[];
		return;
	}
	@weakify(self)
	TableViewCellData * data = [[TableViewCellData alloc] init];
	[data setTextBlock:^NSString *{
		@strongify(self)
		if (self.currentPlacemark) {
			return self.placemarkFormattingBlock(self.currentPlacemark);
		} else {
			return self.geocoding ? IMLocalizedString(@"Geocoding...") : IMLocalizedString(@"Geocode not Available");
		}
	}];
	[data setTextColorBlock:^UIColor *{
		@strongify(self)
		if (self.currentPlacemark) {
			id value = self.placemarkTransformationBlock(self.currentPlacemark);
			ComboBoxItem *item = [self.allItems bk_match:^BOOL (ComboBoxItem *item) {
				return [[item.value description] isEqual:[value description]];
			}];
			if (item) {
				return nil;
			}
		}
		return [UIColor lightGrayColor];
	}];
	data.allowsTextLineWrapping = YES;
	[data setAccessoryViewBlock:^UIView *(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
		@strongify(self)
		if (self.geocoding) {
			UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
			[activityIndicatorView startAnimating];
			return activityIndicatorView;
		} else {
			UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
			[button setAttributedTitle:[DrawUtils attributedStringWithFAKIcon:[FAKIonIcons iosNavigateOutlineIconWithSize:24] fillColor:[UIColor darkGrayColor]] forState:UIControlStateNormal];
			[button sizeToFit];
			[button bk_addEventHandler:^(id sender) {
				[self didStartGeocode];
			} forControlEvents:UIControlEventTouchUpInside];
			return button;
		}
		return nil;
	}];
	[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
		@strongify(self)
		if (self.currentPlacemark && !self.geocoding) {
			id value = self.placemarkTransformationBlock(self.currentPlacemark);
			ComboBoxItem *item = [self.allItems bk_match:^BOOL (ComboBoxItem *item) {
				return [[item.value description] isEqual:[value description]];
			}];
			if (item) {
				[self didSelectLocationItem:item onTableView:tableView atIndexPath:indexPath];
			}
		}
	}];
	self.locationCellDataArray = @[data];
}

- (void)updateTableViewCellDataArrayForHistory {
	@weakify(self)
	NSMutableArray *array = [NSMutableArray array];
	for (ComboBoxItem *item in self.historyItems) {
		TableViewCellData *data = [[TableViewCellData alloc] init];
		data.text = item.label;
		data.allowsTextLineWrapping = YES;
		[data setAccessoryViewBlock:^UIView *(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			@strongify(self)
			UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
			[button setAttributedTitle:[DrawUtils attributedStringWithFAKIcon:[FAKIonIcons iosCloseOutlineIconWithSize:24] fillColor:[UIColor darkGrayColor]] forState:UIControlStateNormal];
			[button sizeToFit];
			[button bk_addEventHandler:^(id sender) {
				[self deleteHistoryItem:item onTableView:tableView];
			} forControlEvents:UIControlEventTouchUpInside];
			return button;
		}];
		[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			@strongify(self)
			[self didSelectHistoryItem: item onTableView: tableView atIndexPath: indexPath];
		}];
		[array addObject:data];
	}
	self.historyCellDataArray = array;
}

- (void)updateTableViewCellDataArrayForSuggestion {
	@weakify(self)
	NSMutableArray * array = [NSMutableArray array];
	for (ComboBoxItem *item in self.suggestedItems) {
		TableViewCellData *data = [[TableViewCellData alloc] init];
		data.text = item.label;
		data.allowsTextLineWrapping = YES;
		[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			@strongify(self)
			[self didSelectSuggestionItem: item onTableView: tableView atIndexPath: indexPath];
		}];
		[array addObject:data];
	}
	self.suggestionCellDataArray = [NSArray arrayWithArray:array];
}

- (void)updateTableViewCellDataArrayForIndexedCollation {
	NSMutableArray *arrays = [NSMutableArray array];
	@weakify(self)
	for (NSArray *items in self.sortedArrays) {
		NSMutableArray *array = [NSMutableArray array];
		for (ComboBoxItem *item in items) {
			TableViewCellData *data = [[TableViewCellData alloc] init];
			data.text = item.label;
			data.allowsTextLineWrapping = YES;
			[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
				@strongify(self)
				[self didSelectCollationItem: item onTableView:tableView atIndexPath: indexPath];
			}];
			[array addObject:data];
		}
		[arrays addObject:[NSArray arrayWithArray:array]];
	}
	self.collationCellDataArrays = [NSArray arrayWithArray:arrays];
}

- (void)updateTableViewCellDataArrayBySearchString:(NSString *)searchString {
	if ([StringUtils isBlank:searchString]) {
		self.searchCellDataArray = @[];
		return;
	}
	NSMutableArray *searchItems = [NSMutableArray arrayWithArray:self.allItems];
	if (self.allowsMultipleSelection) {
		[searchItems removeObjectsInArray:self.selectedItems];
	}
	if (searchItems.count < 500) {
		[searchItems filterUsingPredicate:[NSPredicate predicateWithFormat:@"label contains[cd] %@", searchString]];
	} else {
		[searchItems filterUsingPredicate:[NSPredicate predicateWithFormat:@"label beginswith[cd] %@", searchString]];
	}
	[searchItems sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"label" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]]];
	NSRegularExpression *highlightedRegex = [NSRegularExpression regularExpressionWithPattern:searchString options:NSRegularExpressionCaseInsensitive error:NULL];
	NSMutableArray *array = [NSMutableArray array];
	@weakify(self)
	for (ComboBoxItem *item in searchItems) {
		TableViewCellData *data = [[TableViewCellData alloc] init];
		if (highlightedRegex) {
			NSArray *array = [highlightedRegex matchesInString:item.label options:0 range:NSMakeRange(0, item.label.length)];
			NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] im_initWithString:item.label];
			for (NSTextCheckingResult *result in array) {
				[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:result.range];
			}
			data.attributedText = [[NSAttributedString alloc] initWithAttributedString:attributedString];
		} else {
			data.text = item.label;
		}
		data.allowsTextLineWrapping = YES;
		data.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
		[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			@strongify(self)
			[self didSelectSearchItem: item onTableView: tableView];
		}];
		[array addObject:data];
	}
	self.searchCellDataArray = [NSArray arrayWithArray:array];
}

- (void)makeIndexedCollation {
	IMLocalizedIndexedCollation *provider = [[IMLocalizedIndexedCollation alloc] initWithObjects:self.allItems sortedUsingSelector:@selector(label)];
	self.indexTitles = provider.sectionIndexTitles;
	self.sortedArrays = provider.sectionSortedArrays;
}

- (void)scrollToSelectionOnTableView:(UITableView *)tableView {
	NSIndexPath *indexPath = [CocoaUtils indexPathOfObjectIdenticalTo:self.selectedItem fromArrays:self.sortedArrays];
	if (indexPath) {
		[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
	}
}

- (NSArray *)getDataSourceForRowWithTableView:(UITableView *)tableView {
	if (!self.searching) {
		NSMutableArray *array = [NSMutableArray array];
		[array addObject:self.selectionCellDataArray];
		[array addObject:self.locationCellDataArray];
		[array addObject:self.historyCellDataArray];
		[array addObject:self.suggestionCellDataArray];
		[array addObjectsFromArray:self.collationCellDataArrays];
		return [NSArray arrayWithArray:array];
	} else {
		return @[self.searchCellDataArray];
	}
}

#pragma mark - Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return self.cellDataArray.count;
}

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

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
	if (!self.searching && self.allowsIndexedCollation) {
		NSMutableArray *array = [NSMutableArray array];
		[array addObject:UITableViewIndexSearch];
		if (self.indexTitles) {
			[array addObjectsFromArray:self.indexTitles];
		}
		return [NSArray arrayWithArray:array];
	}
	return nil;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
	if (!self.searching && self.allowsIndexedCollation) {
		if (index == 0) {
			if (!CGPointEqualToPoint(self.tableView.contentOffset, CGPointZero)) {
				self.tableView.contentOffset = CGPointZero;
			}
		} else {
			return index + 3;
		}
	}
	return NSNotFound;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
	if (!self.searching) {
		if (section == 0 && self.selectedItems.count > 0) {
			return self.selectionTitle;
		} else if (section == 1 && self.allowsLocation) {
			return self.locationTitle;
		} else if (section == 2 && self.historyItems.count > 0) {
			return self.historyTitle;
		} else if (section == 3 && self.suggestedItems.count > 0) {
			return self.suggestionTitle;
		} else if (section >= 4 && self.indexTitles) {
			return self.indexTitles[section - 4];
		}
	}
	return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
	TableViewCellData *data = self.cellDataArray[indexPath.section][indexPath.row];
	return [UIToolkit tableView:tableView atIndexPath:indexPath rowHeightWithData:data];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	TableViewCellData *data = self.cellDataArray[indexPath.section][indexPath.row];
	return [UIToolkit tableView:tableView atIndexPath:indexPath cellWithData:data];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	[tableView deselectRowAtIndexPath:indexPath animated:YES];
	TableViewCellData *data = self.cellDataArray[indexPath.section][indexPath.row];
	[UIToolkit tableView:tableView atIndexPath:indexPath didSelectWithData:data];
}

#pragma mark - Search controller delegate

- (void)willDismissSearchController:(UISearchController *)searchController {
	self.searchCellDataArray = @[];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
	self.tableView.contentOffset = _restorationContentOffset;
}

- (void)willPresentSearchController:(UISearchController *)searchController {
	self.searchCellDataArray = @[];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
	_restorationContentOffset = self.tableView.contentOffset;
}

#pragma mark - Search results updating delegate

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
	[self updateTableViewCellDataArrayBySearchString:searchController.searchBar.text];
	self.cellDataArray = [self getDataSourceForRowWithTableView:self.tableView];
	
	if (self.searching && !CGPointEqualToPoint(self.tableView.contentOffset, CGPointZero)) {
		self.tableView.contentOffset = CGPointZero;
	}
	[self.tableView reloadData];
}

@end

