//
// AutoCompleteViewController.m
// IvyMedia
//
// Created by haibara on 8/31/14.
//
//

#import "AutoCompleteViewController.h"
#import "Lang.h"
#import "SDK.h"
#import "CocoaUtils.h"
#import "DrawUtils.h"
#import "UIToolkit.h"
#import "DZNEmptyDataSetDefault.h"
#import <AXNavigationBackItemInjection/AXPopNavigationController.h>
#import <libextobjc/EXTScope.h>
#import <PureLayout/PureLayout.h>

@interface AutoCompleteViewController () <UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate, UISearchControllerDelegate, UISearchResultsUpdating, DZNEmptyDataSetDefault> {
	CGPoint _restorationContentOffset;
	BOOL _shouldLayoutSearchText;
}

@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) NSString *selectedItem;

@property (nonatomic, strong) NSArray *collationCellDataArrays;
@property (nonatomic, strong) NSArray *searchCellDataArray;
@property (nonatomic, strong) NSArray *indexTitles;
@property (nonatomic, strong) NSArray *sortedArrays;

@property (nonatomic, assign, readonly) BOOL searching;

@property (nonatomic, copy) void (^didDoneBlock) (NSString *string);
@property (nonatomic, copy) void (^didCancelBlock) (void);
@property (nonatomic, copy) void (^didSelectStringBlock) (NSString *string);
@property (nonatomic, copy) void (^didSearchStringBlock) (NSString *string);

@end

@implementation AutoCompleteViewController

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

		self.selectedItem = @"";
	}
	return self;
}

- (void)viewDidLoad {
	[super viewDidLoad];

	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.enablesReturnKeyAutomatically = NO;
	self.searchController.searchBar.returnKeyType = UIReturnKeyDone;
	self.searchController.searchBar.delegate = self;
	[self.searchController.searchBar sizeToFit];
	[self.view addSubview:self.searchController.searchBar];

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

	_shouldLayoutSearchText = YES;

	self.tableView.emptyDataSetSource = self;
	[self makeIndexedCollation];
	[self buildCellDataSource];
	[self scrollToSelectionOnTableView:self.tableView];
}

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

- (void)viewWillLayoutSubviews {
	[super viewWillLayoutSubviews];

	if (_shouldLayoutSearchText) {
		self.searchController.searchBar.text = self.selectedItem;
		_shouldLayoutSearchText = NO;
	}
}

- (void)viewDidLayoutSubviews {
	[super viewDidLayoutSubviews];
}

#pragma mark - Public

- (void)searchString:(NSString *)value {
	self.selectedItem = value;
}

- (void)setItemsWithArray:(NSArray *)source {
	self.allItems = source;
}

#pragma mark - Property

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

#pragma mark - Action

- (void)changeSearchItem:(NSString *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	// NSLog(@"%@ %@", self.selectedItem, item);
	self.searchController.active = NO;
	if (![self.selectedItem isEqualToString:item]) {
		self.selectedItem = item;
		[self scrollToSelectionOnTableView:tableView];
		_shouldLayoutSearchText = YES;
	}
	if (self.didSelectStringBlock) {
		self.didSelectStringBlock(self.selectedItem);
	}
}

- (void)changeCollationItem:(NSString *)item onTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
	if (self.searchController.active) {
		self.searchController.active = NO;
	}
	if (![self.selectedItem isEqualToString:item]) {
		self.selectedItem = item;
		[tableView reloadRowsAtIndexPaths:tableView.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationNone];
	} else {
		self.selectedItem = @"";
		[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
	}
	self.searchController.searchBar.text = self.selectedItem;
	if (self.didSelectStringBlock) {
		self.didSelectStringBlock(self.selectedItem);
	}
}

#pragma mark - Private

- (void)buildCellDataSource {
	NSMutableArray *arrays = [NSMutableArray array];
	@weakify(self)
	for (NSArray *items in self.sortedArrays) {
		NSMutableArray *array = [NSMutableArray array];
		for (NSString *item in items) {
			TableViewCellData *data = [[TableViewCellData alloc] init];
			data.text = item;
			data.allowsTextLineWrapping = YES;
			[data setAccessoryTypeBlock:^UITableViewCellAccessoryType {
				@strongify(self)
				if ([self.selectedItem isEqualToString:item]) {
					return UITableViewCellAccessoryCheckmark;
				}
				return UITableViewCellAccessoryNone;
			}];
			[data setVaryingHeightIdentifierBlock:^NSString *(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
				@strongify(self)
				return [NSString stringWithFormat:@"%d", [self.selectedItem isEqualToString:item]];
			}];
			[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
				@strongify(self)
				[self changeCollationItem: item onTableView:tableView atIndexPath: indexPath];
			}];
			[array addObject:data];
		}
		[arrays addObject:[NSArray arrayWithArray:array]];
	}
	self.collationCellDataArrays = [NSArray arrayWithArray:arrays];
	self.searchCellDataArray = @[];
}

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

- (void)makeIndexedCollation {
	IMLocalizedIndexedCollation *provider = [[IMLocalizedIndexedCollation alloc] initWithObjects:self.allItems];
	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) {
		return self.collationCellDataArrays;
	} else {
		return @[self.searchCellDataArray];
	}
}

#pragma mark - Search bar delegate

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
	// NSLog(@"searchBarCancelButtonClicked");
	_shouldLayoutSearchText = YES;
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
	// NSLog(@"searchBarSearchButtonClicked");
	self.selectedItem = searchBar.text;
	self.searchController.active = NO;
	_shouldLayoutSearchText = YES;
	if (self.didSearchStringBlock) {
		self.didSearchStringBlock(self.selectedItem);
	} else if (self.didSelectStringBlock) {
		self.didSelectStringBlock(self.selectedItem);
	}
}

#pragma mark - Table view methods

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

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

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
	if (!self.searching) {
		return self.indexTitles;
	}
	return nil;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
	if (!self.searching) {
		return self.indexTitles[section];
	}
	return nil;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	TableViewCellData *data = [self getDataSourceForRowWithTableView:tableView][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 getDataSourceForRowWithTableView:tableView][indexPath.section][indexPath.row];
	[UIToolkit tableView:tableView atIndexPath:indexPath didSelectWithData:data];
}

#pragma mark - Search controller delegate

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

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

#pragma mark - Search results updating delegate

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
	[self updateTableViewCellDataArrayBySearchString:searchController.searchBar.text];

	if (self.searching && !CGPointEqualToPoint(self.tableView.contentOffset, CGPointZero)) {
		self.tableView.contentOffset = CGPointZero;
	}
	[self.tableView reloadData];
}

@end
