#import "ProductSearchListVC.h"
#import "Lang.h"
#import "SDK.h"
#import "BeanUtils.h"
#import "CocoaUtils.h"
#import "DrawUtils.h"
#import "ImageUtils.h"
#import "NetUtils.h"
#import "CalendarViewController.h"
#import "UIConstants.h"
#import "UIToolkit.h"
#import "DZNEmptyDataSetDefault.h"
#import <BlocksKit/BlocksKit+UIKit.h>
#import <BlocksKit/A2DynamicDelegate.h>
#import <libextobjc/EXTScope.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import <PureLayout/PureLayout.h>
#import <YYKit/YYTimer.h>
#import "Utility.h"
#import "AppTracker.h"
#import "CustomIcons.h"
#import "BusWS.h"
#import "BusSearchData.h"
#import "GetPricesResponseJsonMapper.h"
#import "SearchBusJSONMapper.h"
#import "CustomLeftRightTitleToolbar.h"
#import "BusSearchListCell.h"
#import "ProductDetailVC.h"
#import "Vars.h"
#import "FilterViewController.h"
#import "JSBusSiteBridge.h"

@interface ProductSearchListVC () <UITableViewDataSource, UITableViewDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>

@property (nonatomic, strong) CustomLeftRightTitleToolbar *datePickerToolbar;
@property (nonatomic, strong) NSArray *cellDataArray;
@property (nonatomic, strong) YYTimer *refreshPriceTimer;
@property(nonatomic, strong) NSArray *originSearchData;
@property (nonatomic, strong) FilterData *filterData;
@property(nonatomic, strong) NSMutableArray *filterSearchDataArray;
@property (nonatomic, assign) BOOL isInvalid;

@property (nonatomic, strong, readonly) NSArray *searchList;
@property (nonatomic, strong, readonly) NSString *busFrom;
@property (nonatomic, strong, readonly) NSString *busTo;
@property (nonatomic, strong, readonly) NSString *fromName;
@property (nonatomic, strong, readonly) NSString *toName;
@property (nonatomic, strong, readonly) NSString *dateString;
@property (nonatomic, strong, readwrite) NSDate *dateObj;
@property(nonatomic, assign) CGFloat lowestPrice;
@end

@implementation ProductSearchListVC

// The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (instancetype)init {
	if ((self = [super init])) {
		// Custom initialization
		[[Vars appDelegate] applyDefaultConfigurationForViewController:self];
	}
	return self;
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	[super viewDidLoad];

	[[AppTracker sharedInstance] sendView:[self class]];
	
	self.isInvalid = NO;
	if (!self.currentReturnString) {
		self.currentReturnString = @"";
	}

	if (self.isReschedule) {
		self.title = @"Choose New Schedule";
	} else {
		if (!self.is_return) {
			self.title = @"Choose Departing Schedule";
		} else {
			self.title = @"Choose Returning Schedule";
		}
	}
	
	self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[DrawUtils imageWithFAKIcon:[FAKFontAwesome filterIconWithSize:24] fillColor:[UIColor blackColor]] style:UIBarButtonItemStylePlain target:self action:@selector(filterSearchResult)];
	self.navigationItem.rightBarButtonItem.enabled = NO;

	self.datePickerToolbar = [[CustomLeftRightTitleToolbar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), [UIConstants defaultTableViewRowHeight])];
	@weakify(self)
	[self.datePickerToolbar setLeftBarButtonItemHandler:^{
		@strongify(self)
		[self lastDate];
	}];
	[self.datePickerToolbar setRightBarButtonItemHandler:^{
		@strongify(self)
		[self nextDate];
	}];
	[self.datePickerToolbar setTitleBarButtonItemHandler:^{
		@strongify(self)
		[self accessDatePicker];
	}];
	[self checkDateStepButton];
	self.fixedHeaderView = self.datePickerToolbar;

	[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([BusSearchListCell class]) bundle:[NSBundle mainBundle]] forCellReuseIdentifier:NSStringFromClass([BusSearchListCell class])];
	self.tableView.emptyDataSetDelegate = self;
	[self buildCellDataSource];
}

- (void)viewDidAppear:(BOOL)animated {
	[super viewDidAppear:animated];
	// ajax
	if (!self.searchList) {
		[self searchBus];
	} else {
		[self priceTimeOut];
	}
}

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

}

#pragma mark - Property

- (NSArray *)searchList {
	return !self.is_return ? self.productsDepart : self.productsReturn;
}

- (NSString *)busFrom {
	return !self.is_return ? self.departCity : self.returnCity;
}

- (NSString *)busTo {
	return self.is_return ? self.departCity : self.returnCity;
}

- (NSString *)fromName {
	return [Destination cityName:self.busFrom];
}

- (NSString *)toName {
	return [Destination cityName:self.busTo];
}

- (NSString *)dateString {
	return !self.is_return ? self.currentDepartString : self.currentReturnString;
}

- (NSDate *)dateObj {
	return [[DateFormatUtils ISO_DATE_FORMAT] parse:self.dateString];
}

- (void)setDateObj:(NSDate *)dateObj {
	if (!self.is_return) {
		self.currentDepartString = [[DateFormatUtils ISO_DATE_FORMAT] format:dateObj];
	} else {
		self.currentReturnString = [[DateFormatUtils ISO_DATE_FORMAT] format:dateObj];
	}
}

#pragma mark - Action

-(void)filterSearchResult{
	FilterViewController *viewController = [[FilterViewController alloc] init];
	viewController.hidesBottomBarWhenPushed = YES;
	viewController.filterData = self.filterData;
	[viewController setFilterSearchListBlock:^NSInteger(FilterData *filterData) {
		[self filterSearchListWithData:filterData];
		return self.filterSearchDataArray.count;
	}];
	[viewController setDidDoneBlock:^(void) {
		if (!self.is_return) {
			self.productsDepart = self.filterSearchDataArray;
		}else{
			self.productsReturn = self.filterSearchDataArray;
		}
		[self buildCellDataSource];
		[self.tableView reloadData];
	}];
	[self.navigationController pushViewController:viewController animated:YES];
}

-(void)filterSearchListData{
	if (!self.originSearchData) {
		return;
	}
	self.filterData = [[FilterData alloc] init];
	self.filterData.departureTime = @[@0,@24];
	self.filterData.arrivalTime = @[@0,@24];
	
	NSMutableArray *priceArray = [NSMutableArray array];
	[self.originSearchData bk_each:^(BusSearchData *data) {
		[priceArray addObject:@(data.displayPrice)];
	}];
	
	NSInteger minPrice = [[priceArray valueForKeyPath:@"@min.floatValue"] integerValue];
	NSInteger maxPrice = ceil([[priceArray valueForKeyPath:@"@max.floatValue"] floatValue]);
	self.filterData.priceRange = @[@(minPrice),@(maxPrice)];
	self.filterData.priceDefaultRange = @[@(minPrice),@(maxPrice)];
	
	NSMutableArray *durationArray = [NSMutableArray array];
	[self.originSearchData bk_each:^(BusSearchData * searchData) {
		CGFloat durationTime = [self durationTimeFromSearchData:searchData];
		[durationArray addObject:@(durationTime)];
	}];
	NSInteger maxDurationTime = (NSInteger)ceil([[durationArray valueForKeyPath:@"@max.floatValue"] floatValue]);
	NSInteger miniDurationTime = (NSInteger)[[durationArray valueForKeyPath:@"@min.floatValue"] floatValue];
	self.filterData.durationRange = @[@(miniDurationTime),@(maxDurationTime)];
	self.filterData.durationDefaultRange = @[@(miniDurationTime),@(maxDurationTime)];
	
	NSMutableSet *featureSet = [NSMutableSet set];
	[self.originSearchData bk_each:^(BusSearchData * data) {
		NSArray *featureAmenities = [data.additionalAmenities componentsSeparatedByString:@";"];
		[featureSet addObjectsFromArray:featureAmenities];
	}];
	
	NSMutableArray *featureArray = [NSMutableArray array];
	for (NSString *string in featureSet) {
		if ([string isEqualToString:@""]) {
			continue;
		}
		Feature *feature = [[Feature alloc] init];
		feature.id = string;
		feature.isChecked = NO;
		[featureArray addObject:feature];
	}
	self.filterData.feature = featureArray;
	
	NSMutableSet *operatorSet = [NSMutableSet set];
	[self.originSearchData bk_each:^(BusSearchData * data) {
		[operatorSet addObject:data.operator];
	}];
	
	NSMutableArray *operatorArray = [NSMutableArray array];
	for (NSString *operatorName in operatorSet) {
		Operator *operator = [[Operator alloc] init];
		operator.name = operatorName;
		operator.isChecked = YES;
		[operatorArray addObject:operator];
	}
	self.filterData.operator = operatorArray;
}

-(void )filterSearchListWithData:(FilterData *)filterdata{
	self.filterData = filterdata;
	NSMutableArray *filterSearchDataArray = [NSMutableArray array];
	NSInteger filterDepatureTime = [filterdata.departureTime.firstObject integerValue];
	NSInteger filterReturnTime = [filterdata.departureTime.lastObject integerValue];
	CGFloat filterDurationStartTime = [filterdata.durationRange.firstObject floatValue];
	CGFloat filterDurationEndTime = [filterdata.durationRange.lastObject floatValue];
	NSInteger filterArrivalStartTime = [filterdata.arrivalTime.firstObject integerValue];
	NSInteger filterArrivalEndTime = [filterdata.arrivalTime.lastObject integerValue];
	NSInteger filterMinPrice = ceil([filterdata.priceRange.firstObject floatValue]);
	NSInteger filterMaxPrice = ceil([filterdata.priceRange.lastObject floatValue]);
	for (BusSearchData *searchData in self.originSearchData) {
		NSInteger depatureTime = [searchData timeFromString:searchData.displayArrivalTime isArrival:NO];
		NSInteger arrivalTime = [searchData timeFromString:searchData.displayArrivalTime isArrival:YES];

		if (depatureTime < filterDepatureTime || depatureTime > filterReturnTime) {
			continue;
		}
		
		if (arrivalTime < filterArrivalStartTime || arrivalTime > filterArrivalEndTime) {
			continue;
		}

		CGFloat durationTime = [self durationTimeFromSearchData:searchData];
		
		if (filterDurationStartTime > durationTime || filterDurationEndTime < durationTime){
			continue;
		}
		CGFloat price = searchData.displayPrice;
		if (filterMinPrice > price || filterMaxPrice < price) {
			continue;
		}
		
		BOOL operatorMatch = NO;
		for (Operator *filterOperater in filterdata.operator) {
			if (filterOperater.isChecked) {
				NSString *operatorName  = filterOperater.name;
				if ([searchData.operator isEqualToString:operatorName]) {
					operatorMatch = YES;
				}
			}
		}
		if (!operatorMatch) {
			continue;
		}
		
		NSMutableSet *filterSet = [NSMutableSet set];
		for (Feature *feature in filterdata.feature) {
			if (feature.isChecked && ![StringUtils isEmpty:feature.id]) {
				[filterSet addObject:feature.id];
			}
		}
		
		NSArray *featureAmenities = [searchData.additionalAmenities componentsSeparatedByString:@";"];
		NSMutableSet *featureSet = [NSMutableSet set];
		for (NSString *amenities in featureAmenities) {
			if ([amenities isEqualToString:@""]) {
				continue;
			}
			if (amenities) {
				[featureSet addObject:amenities];
			}
		}
		if (![filterSet isSubsetOfSet:featureSet]) {
			continue;
		}

		[filterSearchDataArray addObject:searchData];
	}
	self.filterSearchDataArray = filterSearchDataArray;
}


#pragma mark - Segue

- (void)performSegueForDetailWithObject:(BusSearchData *)search {
	if (self.isReschedule) {
	} else {
		if (self.roundtrip && self.is_return) {
			CartEntry *entry = [[Vars appDelegate].shopCarts.entries bk_match:^BOOL (CartEntry *obj) {
				return obj.product.purchasedSeparately;
			}];
			if (entry && search.vendorId != entry.product.vendor) {
				[CocoaUtils showAlertWithTitle:IMLocalizedString(@"Information") message:[NSString stringWithFormat:@"Sorry! %@ tickets cannot be combined with other services. You may select %@ tickets for your round-trip, or try to book the other service separately. We are working on a solution; thanks for your patience!", entry.product.vendor_obj.short_name, entry.product.vendor_obj.short_name]];
				return;
			} else if (!entry && search.purchasedSeparately) {
				[CocoaUtils showAlertWithTitle:IMLocalizedString(@"Information") message:[NSString stringWithFormat:@"Sorry! %@ tickets cannot be combined with other services. You may select another schedule, or book %@ tickets only for your round-trip. We are working on a solution; thanks for your patience!", search.operator, search.operator]];
				return;
			}
		}
	}
    ProductDetailVC *viewController = [[ProductDetailVC alloc]init];
	viewController.beforethisdate = self.currentDepartString;
	viewController.return_date = self.currentReturnString;
	viewController.depart = self.departCity;
	viewController.arrival = self.returnCity;
	viewController.is_roundtrip = self.roundtrip;
	viewController.prData = search;
	viewController.is_return = self.is_return;
	viewController.isReschedule = self.isReschedule;
	viewController.isBusHotelPackage = self.isBusHotelPackage;
    viewController.isFlexiTicketOnly = self.isFlexiTicketOnly;
    [self.navigationController pushViewController:viewController animated:YES];
}

#pragma mark - Network

- (void)searchBus {
	self.isInvalid = NO;
	NSInteger vendorId = 0;
	if (self.vendorId > 0) {
		vendorId = self.vendorId;
	} else if ([Vars clientDomainId] > 0) {
		vendorId = [Vars clientDomainId];
	}
	SearchBus *searchBus = [[SearchBus alloc] init];
	searchBus.isRoundTrip = self.roundtrip;
	searchBus.departureCity = self.departCity;
	searchBus.arrivalCity = self.returnCity;
	searchBus.startDate = self.currentDepartString;
	searchBus.returnDate = self.currentReturnString;
	searchBus.vendorId = vendorId;
	searchBus.isRescheduleBus = self.isReschedule;
	searchBus.isBusHotelPackage = self.isBusHotelPackage;
	if (self.pageUrl) {
		searchBus.pageUrl = self.pageUrl;
	}

	NSDictionary *requestDict = [BeanUtils describeAccessibleSimple:searchBus];
	// NSLog(@"%@", requestDict);
	self.tableView.emptyDataSetSource = nil;
	[Utility performSOAPRequestWithUrl:searchBus.address namespace:searchBus.namespace name:searchBus.name parameters:requestDict success:^(NSString *xml) {
		NSString *responseStr = xml;
		// NSLog(@"%@", responseStr);
		NSString *ret = [CocoaUtils firstValueMatchedInString:responseStr forElement:@"return"];
		if (ret) {
			NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:[ret dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
			if (jsonArray.count > 0) {
				NSDictionary *jsonDict = jsonArray.firstObject;
				// NSLog(@"%@", jsonDict);
				SearchBusJSONMapper *mapper = [[SearchBusJSONMapper alloc] init];
				if (![mapper parseJSONObject:jsonDict]) {
					SearchBusResponse *response = mapper.response;
					self.productsDepart = response.departureList.route;
					self.productsReturn = response.arrivalList.route;
					if (![StringUtils isEmpty:response.departureCity]) {
						self.departCity = response.departureCity;
					}
					if (![StringUtils isEmpty: response.arrivalCity]) {
						self.returnCity = response.arrivalCity;
					}
					self.originSearchData = self.searchList;
					[self priceTimeOut];
					[self filterSearchListData];
					if (self.searchList.count) {
						self.navigationItem.rightBarButtonItem.enabled = YES;
					}else{
						self.navigationItem.rightBarButtonItem.enabled = NO;
					}
					BOOL hasDynamic = [self.searchList bk_any:^BOOL (BusSearchData *data) {
						return data.dynamic;
					}];
					BOOL hasAi = [self.searchList bk_any:^BOOL (BusSearchData *data) {
						return data.ai;
					}];
					if (hasDynamic && hasAi) {
						[NetUtils beginTransation];
						[NetUtils performBatchOf:@[[self performRequestForGetPrices:nil], [self performRequestForDynamicPrices:nil]] completion:^{
							[self buildCellDataSource];
							self.tableView.emptyDataSetSource = self;
							[self.tableView reloadData];
						}];
					} else if (hasDynamic) {
						[self performRequestForDynamicPrices:^{
							[self buildCellDataSource];
							self.tableView.emptyDataSetSource = self;
							[self.tableView reloadData];
						}];
					} else if (hasAi) {
						[self performRequestForGetPrices:^{
							[self buildCellDataSource];
							self.tableView.emptyDataSetSource = self;
							[self.tableView reloadData];
						}];
					} else {
						[self buildCellDataSource];
						self.tableView.emptyDataSetSource = self;
						[self.tableView reloadData];
					}
				}
			}
		}
	}];
}

- (id)performRequestForDynamicPrices:(void (^)(void))completion {
	// https://www.gotobus.com/cgi-bin/ajax.fcgi?type=dynamic_prices&version=1&pids=30996&service_date=2018-04-12

	NSArray *searArray = [self.searchList bk_select:^BOOL (BusSearchData *data) {
		return data.dynamic;
	}];
	NSString *pids = [[searArray valueForKeyPath:@"@unionOfObjects.pid"] componentsJoinedByString:@"|"];

	if ([StringUtils isEmpty:@"self.dateString"]) {
		return nil;
	}
	
	NSDictionary *requestDict = @{@"type" : @"dynamic_prices",@"version" : @1,@"pids" : pids,@"service_date" : self.dateString};

	return [Utility getJSONFromRequestWithMethod:@"GET" url:[Utility perlCgiBin:@"ajax"] parameters:requestDict success:^(id jsonObject) {
		GetPricesResponseJsonMapper *mapper = [[GetPricesResponseJsonMapper alloc] init];
		if (![mapper parseWithJSONObject:jsonObject]) {
			GetPricesResponse *response = mapper.response;
			for (BusSearchData *data in self.searchList) {
				for (ProductAiPriceBean *bean in response.prices) {
					if (bean.pid == data.pid) {
						if (data.peopleType.count > bean.price.count) {
							data.peopleType = [data.peopleType subarrayWithRange:NSMakeRange(0, bean.price.count)];
						}
						if (data.peopleType.count == bean.price.count) {
							[data.peopleType enumerateObjectsUsingBlock:^(GetProductOptionsResponsePeopleType *obj, NSUInteger idx, BOOL *stop) {
								GetProductOptionsResponsePeopleType *peopleType = bean.price[idx];
								obj.peoplePrice = peopleType.peoplePrice;
							}];
							data.dynamic = NO;
						}
						break;
					}
				}
			}
			if (completion) {
				completion();
			}
		}
	}];
}

- (id)performRequestForGetPrices:(void (^)(void))completion {
	// https://www.gotobus.com/app/do/ajax/calculate-price/get-prices?pids=54407&service_date=2018-04-12

	NSArray *searArray = [self.searchList bk_select:^BOOL (BusSearchData *data) {
		return data.ai;
	}];
	NSString *pids = [[searArray valueForKeyPath:@"@unionOfObjects.pid"] componentsJoinedByString:@"|"];

	GetPrices *getPrices = [[GetPrices alloc] init];
	getPrices.pids = pids;
	getPrices.service_date = self.dateString;

	NSDictionary *requestDict = [BeanUtils describeAccessibleSimple:getPrices];

	return [Utility performRESTRequestWithMethod:@"GET" url:getPrices.address parameters:requestDict success:^(id jsonObject) {
		GetPricesResponseJsonMapper *mapper = [[GetPricesResponseJsonMapper alloc] init];
		if (![mapper parseWithJSONObject:jsonObject]) {
			GetPricesResponse *response = mapper.response;
			for (BusSearchData *data in self.searchList) {
				for (ProductAiPriceBean *bean in response.prices) {
					if (bean.pid == data.pid) {
						if (data.peopleType.count == bean.price.count) {
							[data.peopleType enumerateObjectsUsingBlock:^(GetProductOptionsResponsePeopleType *obj, NSUInteger idx, BOOL *stop) {
								GetProductOptionsResponsePeopleType *peopleType = bean.price[idx];
								obj.peoplePrice = peopleType.peoplePrice;
							}];
							data.ai = NO;
						}
						break;
					}
				}
			}
			if (completion) {
				completion();
			}
		}
	}];
}

#pragma mark - Private

- (void)buildCellDataSource {
	NSMutableArray *array = [NSMutableArray array];
	BOOL isShowDynamic = [self.searchList bk_any:^BOOL (BusSearchData *search) {
		return search.isDynamicPrice;
	}];
	if (isShowDynamic) {
		TableViewCellData *data = [[TableViewCellData alloc] init];
		data.style = UITableViewCellStyleSubtitle;
		data.image = [DrawUtils tableViewCellImageWithFAKIcon:[CustomIcons dynamicPriceIconWithSize:24] fillColor:[CocoaUtils colorWithRGBs:StandardColorBlue]];
		data.text = @"Dynamic Price";
		data.detailText = @"Price not guaranteed until checkout is complete.";
		data.detailTextColor = [UIColor darkGrayColor];
		data.adjustsDetailTextFontSize = YES;
		[array addObject:data];
	}

    NSMutableArray *priceArray = [NSMutableArray array];
    [self.searchList bk_each:^(BusSearchData *obj) {
        [priceArray addObject:@(obj.displayPrice)];
    }];
    self.lowestPrice = [[priceArray valueForKeyPath:@"@min.floatValue"] floatValue];;
    
	@weakify(self)
	for (BusSearchData *search in self.searchList) {
		TableViewCellData *data = [[TableViewCellData alloc] init];
		data.reuseIdentifier = NSStringFromClass([BusSearchListCell class]);
		data.weaklyAssociatedObject = search;
		[data setNeedLayoutFittingHeight];
		[data setDidSelectBlock:^(UITableView *tableView, NSIndexPath *indexPath, TableViewCellData *data) {
			@strongify(self)
			[self performSegueForDetailWithObject: search];
		}];
        
		[array addObject:data];
	}
	self.cellDataArray = [NSArray arrayWithArray:array];
}

- (void)accessDatePicker {
	CalendarViewController *viewController = [[CalendarViewController alloc] init];
	viewController.title = !self.is_return ? @"Departure Date" : @"Return Date";
	[[Vars appDelegate] applyDefaultConfigurationForViewController:viewController];
	viewController.calendarManager.minimumDate = (self.isBusHotelPackage && self.is_return) ? [DateUtils addDays:[[DateFormatUtils ISO_DATE_FORMAT] parse:self.currentDepartString] amount:1] : [NSDate date];
	viewController.calendarManager.maximumDate = [DateUtils addMonths:[NSDate date] amount:6];
	[viewController.calendarManager setDidSelectDateBlock:^(NSDate *date) {
		[self.navigationController popViewControllerAnimated:YES];
		if (self.isBusHotelPackage && !self.is_return) {
			if ([date compare:[[DateFormatUtils ISO_DATE_FORMAT] parse:self.currentReturnString]] != NSOrderedAscending) {
				self.currentReturnString = [[DateFormatUtils ISO_DATE_FORMAT] format:[DateUtils addDays:date amount:1]];
			}
		}
		[self refreshWithDate:date];
	}];
	if (self.dateObj) {
		[viewController selectDate:self.dateObj];
	}
	[self.navigationController pushViewController:viewController animated:YES];
}

- (void)nextDate {
	NSDate *nextDate = [DateUtils addDays:self.dateObj amount:1];
	if (self.isBusHotelPackage && !self.is_return) {
		if ([nextDate compare:[[DateFormatUtils ISO_DATE_FORMAT] parse:self.currentReturnString]] != NSOrderedAscending) {
			self.currentReturnString = [[DateFormatUtils ISO_DATE_FORMAT] format:[DateUtils addDays:nextDate amount:1]];
		}
	}
	[self refreshWithDate:nextDate];
}

- (void)lastDate {
	[self refreshWithDate:[DateUtils addDays:self.dateObj amount:-1]];
}

- (void)refreshWithDate:(NSDate *)date {
	self.dateObj = date;
	[self checkDateStepButton];
	[self searchBus];
}

- (void)checkDateStepButton {
	NSDate *minimumDate = nil;
	if (self.isBusHotelPackage && self.is_return) {
		minimumDate = [DateUtils addDays:[[DateFormatUtils ISO_DATE_FORMAT] parse:self.currentDepartString] amount:1];
	} else {
		minimumDate = [NSDate date];
	}
	self.datePickerToolbar.leftBarButtonItem.enabled = [DateUtils truncated:self.dateObj compareTo:minimumDate unit:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay] == NSOrderedDescending;
	self.datePickerToolbar.rightBarButtonItem.enabled = [DateUtils truncated:self.dateObj compareTo:[DateUtils addMonths:[NSDate date] amount:6] unit:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay] == NSOrderedAscending;
	self.datePickerToolbar.titleBarButtonItem.title = [Utility localizedStringWithDate:self.dateObj];
}

- (void)priceTimeOut {
	NSInteger timeout = 5 * 60;
	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.refreshPriceTimer = [YYTimer timerWithTimeInterval:timeout target:self selector:@selector(showPlaceholders) repeats:YES];
		[[NSRunLoop mainRunLoop] addTimer:self.refreshPriceTimer forMode:NSRunLoopCommonModes];
		[self.refreshPriceTimer fire];
	});
}

- (void)showPlaceholders {
	self.isInvalid = YES;
	self.cellDataArray = @[];
	self.tableView.emptyDataSetSource = self;
	[self.tableView reloadData];
}

-(CGFloat)durationTimeFromSearchData:(BusSearchData *)searchData{
	NSLocale *locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
	NSDate *startDate = [DateUtils parseDate:[NSString stringWithFormat:@"%@ %@", self.dateString, [searchData lastDepartureTimeByCityName:self.fromName]] locale:locale parsePatterns:@"yyyy-MM-dd h:mma", nil];
	NSInteger nextDay = [searchData nextArrivalDayByCityName:self.toName];
	NSDate *endDate = [DateUtils addDays:[DateUtils parseDate:[NSString stringWithFormat:@"%@ %@", self.dateString, [searchData displayTimeByCityName:self.toName isArrival:YES]] locale:locale parsePatterns:@"yyyy-MM-dd h:mma", nil] amount:nextDay];
	CGFloat durationTime = [Utility abbreviatedDurationTimeFromDate:startDate toDate:endDate];
	return durationTime;
}

#pragma mark - Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}

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

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
	if (section == 0 && self.fromName) {
		return [NSString stringWithFormat:@"%@ to %@", self.fromName, self.toName];
	}
	return nil;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
	TableViewCellData *data = self.cellDataArray[indexPath.row];
	if ([data.reuseIdentifier isEqualToString:NSStringFromClass([BusSearchListCell class])]) {
		return [UIToolkit instantiateRowHeightForTableViewCellWithNibName:NSStringFromClass([BusSearchListCell class])];
	}
    return [UIConstants defaultTableViewRowHeight];
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	TableViewCellData *data = self.cellDataArray[indexPath.row];
	if ([data.reuseIdentifier isEqualToString:NSStringFromClass([BusSearchListCell class])]) {
		BusSearchData *search = data.weaklyAssociatedObject;
		BusSearchListCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([BusSearchListCell class])];
		if (YES) {
			NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
			[attributedString appendAttributedString:[[NSAttributedString alloc] im_initWithString:[search displayTimeByCityName:self.fromName isArrival:NO]]];
			if (YES) {
				[attributedString appendAttributedString:[[NSAttributedString alloc] im_initWithString:@" "]];
				UIImage *image = [DrawUtils imageWithFAKIcon:[FAKFontAwesome longArrowRightIconWithSize:17] fillColor:[UIColor blackColor]];
				[attributedString appendAttributedString:[CocoaUtils attributedStringForImage:[ImageUtils scaleImage:image byFactor:cell.departTimeLabel.font.pointSize / image.size.height] verticalAlignWithFont:cell.departTimeLabel.font]];
			}
			[attributedString appendAttributedString:[[NSAttributedString alloc] im_initWithString:@" "]];
			[attributedString appendAttributedString:[[NSAttributedString alloc] im_initWithString:[search displayTimeByCityName:self.toName isArrival:YES]]];
			NSInteger nextDay = [search nextArrivalDayByCityName:self.toName];
			if (nextDay > 0) {
				[attributedString appendAttributedString:[[NSAttributedString alloc] im_initWithString:@" "]];
				if (nextDay == 1) {
					[attributedString appendAttributedString:[DrawUtils attributedStringWithFAKIcon:[CustomIcons nextDayIconWithSize:cell.departTimeLabel.font.pointSize] fillColor:[CocoaUtils colorWithRGBs:StandardColorBlue]]];
				} else if (nextDay == 2) {
					[attributedString appendAttributedString:[DrawUtils attributedStringWithFAKIcon:[CustomIcons nextDayTwoIconWithSize:cell.departTimeLabel.font.pointSize] fillColor:[CocoaUtils colorWithRGBs:StandardColorBlue]]];
				}
			}
			cell.departTimeLabel.attributedText = [[NSAttributedString alloc] initWithAttributedString:attributedString];
		}
		if (YES) {
			NSLocale *locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
			NSDate *starDate = [DateUtils parseDate:[NSString stringWithFormat:@"%@ %@", self.dateString, [search lastDepartureTimeByCityName:self.fromName]] locale:locale parsePatterns:@"yyyy-MM-dd h:mma", nil];
			NSInteger nextDay = [search nextArrivalDayByCityName:self.toName];
			NSDate *endDate = [DateUtils addDays:[DateUtils parseDate:[NSString stringWithFormat:@"%@ %@", self.dateString, [search displayTimeByCityName:self.toName isArrival:YES]] locale:locale parsePatterns:@"yyyy-MM-dd h:mma", nil] amount:nextDay];
			cell.durationLabel.text = [Utility abbreviatedDurationStringFromDate:starDate toDate:endDate];
		}
		if (YES) {
            cell.scheduleCityLabel.text = [search locationByCityName:self.fromName isArrival:NO];
			cell.arrivalLabel.text = [search locationByCityName:self.toName isArrival:YES];
		}
		if (YES) {
			if (search.isDynamicPrice) {
				cell.priceLabel.attributedText = [DrawUtils attributedStringWithFAKIcon:[CustomIcons dynamicPriceIconWithSize:24] fillColor:[CocoaUtils colorWithRGBs:StandardColorBlue]];
			} else {
                UIColor *priceColor = [Vars defaultTintColor];
                if (search.displayPrice == self.lowestPrice) {
                    priceColor = [Utility lowestPriceColor];
                }
				UIFont *integerDigitsFont = cell.priceLabel.font;
				UIFont *fractionDigitsFont = [integerDigitsFont fontWithSize:integerDigitsFont.pointSize * 0.5];
                cell.priceLabel.attributedText = [Utility attributedPriceWithNumber:[NSNumber numberWithFloat:search.displayPrice] currencySymbol:search.currencySymbol integerDigitsFont:integerDigitsFont fractionDigitsFont:fractionDigitsFont textColor:priceColor];
			}
		}
        if ([search isNearbyByCityName:self.fromName isArrival:NO]) {
            cell.locationImageView.image = [DrawUtils imageWithFAKIcon:[FAKFontAwesome mapMarkerIconWithSize:cell.scheduleCityLabel.font.pointSize] fillColor:[Vars defaultTintColor]] ;
		} else {
			cell.locationImageView.image = nil;
		}
        if ([search isNearbyByCityName:self.toName isArrival:YES]) {
            cell.arrivalImageview.image = [DrawUtils imageWithFAKIcon:[FAKFontAwesome mapMarkerIconWithSize:cell.arrivalLabel.font.pointSize] fillColor:[Vars defaultTintColor]] ;
        } else {
            cell.arrivalImageview.image = nil;
        }
        if (YES) {
			[cell.operImageView sd_setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://www.gotobus.com/images/logo/vendor/%d.jpg", search.vendorId]] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
				if (image) {
					cell.busOperatorLabel.text = nil;
					cell.busOperatorLabel.hidden = YES;
					cell.operImageView.hidden = NO;
				} else {
					cell.busOperatorLabel.text = search.operator;
					cell.busOperatorLabel.hidden = NO;
					cell.operImageView.hidden = YES;
				}
			}];
        }
		return cell;
	}
	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.row];
	[UIToolkit tableView:tableView atIndexPath:indexPath didSelectWithData:data];
}

#pragma mark - <DZNEmptyDataSet>

- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
	if (self.isInvalid) {
		return [[NSAttributedString alloc] im_initWithString:@"Your session has timed out." attributes:@{ NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleBody], NSForegroundColorAttributeName : [UIColor darkGrayColor] }];
	}
	return [[NSAttributedString alloc] im_initWithString:IMLocalizedString(@"No schedules were found that matched your search") attributes:@{ NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleBody], NSForegroundColorAttributeName : [UIColor lightGrayColor] }];
}

- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView {
	if (self.isInvalid) {
		return [[NSAttributedString alloc] im_initWithString:@"Please try your search again." attributes:@{ NSFontAttributeName : [UIFont preferredFontForTextStyle:UIFontTextStyleBody], NSForegroundColorAttributeName : [UIColor lightGrayColor] }];
	}
	return nil;
}

- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
	if (self.isInvalid) {
		return [DrawUtils imageWithFAKIcon:[FAKFontAwesome clockOIconWithSize:100] fillColor:[UIColor darkGrayColor]];
	}
	return [DrawUtils imageWithFAKIcon:[FAKFontAwesome fileOIconWithSize:100] fillColor:[UIColor mercuryColor]];

}

- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
	if (self.isInvalid) {
		return [[NSAttributedString alloc] im_initWithString:@"Refresh Search" attributes:@{ NSFontAttributeName : [UIFont boldSystemFontOfSize:[UIFont buttonFontSize]], NSForegroundColorAttributeName : [Vars defaultTintColor] }];
	}
	return nil;
}

- (void)emptyDataSetDidAppear:(UIScrollView *)scrollView {
	if (self.isInvalid) {
		UIButton *refreshButton = [scrollView valueForKeyPath:@"emptyDataSetView.button"];
		[Utility addRoundedBorderToView:refreshButton];
		CGRect buttonFrame = refreshButton.frame;
		buttonFrame.size.width -= 140;
		buttonFrame.origin.x += 70;
		refreshButton.frame = buttonFrame;
	}
}

- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
	if (self.isInvalid) {
		[self searchBus];
	}
}


@end
