//
//  ZBJCalendarView.m
//  ZBJCalendar
//
//  Created by gumpwang on 2/24/16.
//  Copyright © 2016 ZBJ. All rights reserved.
//

#import "ZBJCalendarView.h"
#import "NSDate+ZBJAddition.h"
#import "NSDate+IndexPath.h"

@interface ZBJCalendarView () <UICollectionViewDataSource, UICollectionViewDelegate, ZBJCalendarWeekViewDelegate>

@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSString *cellIdentifier;
@property (nonatomic, strong) NSString *sectionHeaderIdentifier;
@property (nonatomic, strong) NSString *sectionFooterIdentifier;

@end

@implementation ZBJCalendarView

#pragma  mark - Override
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
        [self addSubview:self.collectionView];
        [self addSubview:self.weekView];
        
        self.selectionMode = ZBJSelectionModeMutiple;
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    // weekViewFrame
    if (self.weekViewHeight > 0) {
        self.weekView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), self.weekViewHeight);
    } else {
        self.weekView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 46);
    }
    
    // collecitonViewFrame
    self.collectionView.frame = CGRectMake(0,
                                           CGRectGetMaxY(self.weekView.frame),
                                           CGRectGetWidth(self.frame),
                                           CGRectGetHeight(self.frame) - CGRectGetMaxY(self.weekView.frame));
    
    // cellWidth
    NSInteger collectionContentWidth = CGRectGetWidth(self.collectionView.frame) - self.contentInsets.left - self.contentInsets.right;
    NSInteger residue = collectionContentWidth % 7;

    CGFloat cellWidth = collectionContentWidth / 7.0;
    if (residue != 0) {
        
        CGFloat newPadding;
        if (residue > 7.0 / 2) {
            newPadding = self.contentInsets.left - (7 - residue) / 2.0;
            cellWidth = (collectionContentWidth + 7 - residue) / 7.0;
        } else {
            newPadding = self.contentInsets.left + (residue / 2.0);
            cellWidth = (collectionContentWidth - residue) / 7.0;
        }
        
        UIEdgeInsets inset = UIEdgeInsetsMake(self.contentInsets.top, newPadding, self.contentInsets.bottom, newPadding);
        self.contentInsets = inset;
    }
    self.collectionView.contentInset = self.contentInsets;
    self.collectionView.scrollIndicatorInsets = UIEdgeInsetsMake(self.contentInsets.top, 0, self.contentInsets.bottom, 0);
    self.weekView.contentInsets = self.contentInsets;
    
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    
    // cellHeight
    if (self.cellScale > 0) {
        layout.itemSize = CGSizeMake(cellWidth, (int)(cellWidth * self.cellScale));
    } else {
        layout.itemSize = CGSizeMake(cellWidth, cellWidth);
    }

    self.collectionView.collectionViewLayout = layout;
    
    if (!CGPointEqualToPoint(self.collectionView.contentOffset, CGPointMake(self.contentOffset.x - self.contentInsets.left, self.contentOffset.y - self.contentInsets.top))) {
        self.collectionView.contentOffset = CGPointMake(self.contentOffset.x - self.contentInsets.left, self.contentOffset.y - self.contentInsets.top);
    }
}

#pragma mark - public method
- (void)registerCellClass:(id)clazz withReuseIdentifier:(NSString *)identifier {
    self.cellIdentifier = identifier;
    [self.collectionView registerClass:clazz forCellWithReuseIdentifier:identifier];
}

- (void)registerSectionHeader:(id)clazz withReuseIdentifier:(NSString *)identifier{
    self.sectionHeaderIdentifier = identifier;
    [self.collectionView registerClass:clazz forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:identifier];
}

- (void)registerSectionFooter:(id)clazz withReuseIdentifier:(NSString *)identifier{
    self.sectionFooterIdentifier = identifier;
    [self.collectionView registerClass:clazz forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:identifier];
}

- (void)setWeekViewHeight:(CGFloat)weekViewHeight {
    _weekViewHeight = weekViewHeight;
    self.weekView.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), _weekViewHeight);
}

- (void)setMinimumLineSpacing:(CGFloat)minimumLineSpacing {
    _minimumLineSpacing = minimumLineSpacing;
    
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    if (_minimumLineSpacing > 0) {
        layout.minimumLineSpacing = _minimumLineSpacing;
    } else {
        layout.minimumLineSpacing = 0;
    }
    self.collectionView.collectionViewLayout = layout;
}

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    self.collectionView.backgroundColor = backgroundColor;
    self.weekView.backgroundColor = backgroundColor;
}

- (void)setAllowsSelection:(BOOL)allowsSelection {
    _allowsSelection = allowsSelection;
    self.collectionView.allowsSelection = _allowsSelection;
}

- (void)setDataSource:(id<ZBJCalendarDataSource>)dataSource {
    _dataSource = dataSource;
    self.weekView.delegate = self;
}

- (void)reloadData {
    [self.collectionView reloadData];
}

- (id)cellAtDate:(NSDate *)date {
    NSIndexPath *indexPath = [NSDate indexPathAtDate:date firstDate:self.firstDate];
    return [self.collectionView cellForItemAtIndexPath:indexPath];
}

- (void)reloadItemsAtDates:(NSSet<NSDate *> *)dates {
    NSMutableArray *indexPaths = [NSMutableArray new];
    for (NSDate *date in dates) {
        NSIndexPath *indexPath = [NSDate indexPathAtDate:date firstDate:self.firstDate];
        [indexPaths addObject:indexPath];
    }
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
}

- (void)reloadItemsAtMonths:(NSSet<NSDate *> *)months {
    NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
    for (NSDate *date in months) {
        NSIndexPath *indexPath = [NSDate indexPathAtDate:date firstDate:self.firstDate];
        [indexSet addIndex:indexPath.section];
    }
    [self.collectionView reloadSections:indexSet];
}

- (void)setContentOffset:(CGPoint)contentOffset {
    _contentOffset = contentOffset;
    CGPoint origin = CGPointMake(_contentOffset.x - self.contentInsets.left, _contentOffset.y - self.contentInsets.top);
    [self.collectionView setContentOffset:origin];
}


- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated {
    _contentOffset = contentOffset;
    CGPoint origin = CGPointMake(_contentOffset.x - self.contentInsets.left, _contentOffset.y - self.contentInsets.top);
    [self.collectionView setContentOffset:origin animated:YES];
}

- (CGSize)contentSize {
    return self.collectionView.contentSize;
}

#pragma mark - private methods
- (NSDate *)dateForCollectionView:(UICollectionView *)collection atIndexPath:(NSIndexPath *)indexPath {
    NSDate *date = nil;
    
    // if headStyle is `ZBJCalendarViewHeadStyleCurrentWeek`, the first month is special
    if (self.headStyle == ZBJCalendarViewHeadStyleCurrentWeek && indexPath.section == 0) {
        NSDate *firstDay = [self.firstDate firstDateOfWeek];
        NSDate *lastDateOfMonth = [self.firstDate lastDateOfMonth];
        NSInteger items = [NSDate numberOfNightsFromDate:firstDay toDate:lastDateOfMonth];
        if (indexPath.row > items) {
        } else {

            NSCalendar *calendar = [NSDate gregorianCalendar];
            NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitDay fromDate:firstDay];
            [components setDay:indexPath.row];
            [components setMonth:indexPath.section];
            date = [calendar dateByAddingComponents:components toDate:firstDay options:0];
            
            if (![self.firstDate isSameMonthWithDate:date]) {
                date = nil;
            }
        }
    } else { // normal logic
        date = [NSDate dateAtIndexPath:indexPath firstDate:self.firstDate];
    }

    return date;
}

#pragma mark UICollectionViewDataSource

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    
    NSDate *firstDateOfMonth = [NSDate dateForFirstDayInSection:indexPath.section firstDate:self.firstDate];
    
    if (self.sectionHeaderIdentifier && [kind isEqualToString:UICollectionElementKindSectionHeader]) {
        id headerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:self.sectionHeaderIdentifier forIndexPath:indexPath];
        if (self.dataSource && [self.dataSource respondsToSelector:@selector(calendarView:configureSectionHeaderView:firstDateOfMonth:)]) {
            [self.dataSource calendarView:self configureSectionHeaderView:headerView firstDateOfMonth:firstDateOfMonth];
        }
        return headerView;
    } else if (self.sectionFooterIdentifier && [kind isEqualToString:UICollectionElementKindSectionFooter]) {
        id footerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:self.sectionFooterIdentifier forIndexPath:indexPath];
        if (self.dataSource && [self.dataSource respondsToSelector:@selector(calendarView:configureSectionFooterView:lastDateOfMonth:)]) {
            [self.dataSource calendarView:self configureSectionFooterView:footerView lastDateOfMonth:[firstDateOfMonth lastDateOfMonth]];
        }
        return footerView;
    }
   
    return NULL;
}


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    
    // if headStyle is `ZBJCalendarViewHeadStyleCurrentWeek`, the first month is special
    if (self.headStyle == ZBJCalendarViewHeadStyleCurrentWeek && section == 0) {
        NSInteger weekDay = [self.firstDate weekday];
        NSDate *lastDateOfMonth = [self.firstDate lastDateOfMonth];
        NSInteger lastDateOfMonthWeekDay = [lastDateOfMonth weekday];
        NSInteger items = weekDay + [NSDate numberOfNightsFromDate:self.firstDate toDate:lastDateOfMonth] + 7 - lastDateOfMonthWeekDay;
        return items;
    }
    
    // normal logic
    NSDate *firstDay = [NSDate dateForFirstDayInSection:section firstDate:self.firstDate];
    NSInteger weekDay = [firstDay weekday] -1;
    
    NSDate *lastDateOfMonth = [firstDay lastDateOfMonth];
    NSInteger lastWeekDay = [lastDateOfMonth weekday];
    
    NSInteger items =  weekDay + [NSDate numberOfDaysInMonth:firstDay] + 7 - lastWeekDay;    return items;
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    NSInteger months = [NSDate numberOfMonthsFromDate:self.firstDate toDate:self.lastDate];
    return months;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    id cell = [collectionView dequeueReusableCellWithReuseIdentifier:self.cellIdentifier forIndexPath:indexPath];
    
    NSDate *date = [self dateForCollectionView:collectionView atIndexPath:indexPath];
    
    if (self.dataSource && [self.dataSource respondsToSelector:@selector(calendarView:configureCell:forDate:)]) {
        [self.dataSource calendarView:self configureCell:cell forDate:date];
    }
   
    return cell;
}


#pragma mark - UICollectionViewDelegate

- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    
    NSDate *date = [self dateForCollectionView:collectionView atIndexPath:indexPath];
 
    if (self.delegate && [self.delegate respondsToSelector:@selector(calendarView:shouldSelectDate:)]) {
        return [self.delegate calendarView:self shouldSelectDate:date];
    }
    
    return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}


- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
   
    NSDate *date = [self dateForCollectionView:collectionView atIndexPath:indexPath];
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(calendarView:didSelectDate:ofCell:)]) {
        id cell = [collectionView cellForItemAtIndexPath:indexPath];
        [self.delegate calendarView:self didSelectDate:date ofCell:cell];
    }
}


#pragma mark UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    if (self.sectionHeaderIdentifier) {
        if (self.sectionHeaderHeight > 0) {
            return CGSizeMake(CGRectGetWidth(collectionView.frame), self.sectionHeaderHeight);
        } else {
            return CGSizeMake(CGRectGetWidth(collectionView.frame), 52);
        }
    }
    return CGSizeZero;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    if (self.sectionFooterIdentifier) {
        if (self.sectionFooterIdentifier > 0) {
            return CGSizeMake(CGRectGetWidth(collectionView.frame), self.sectionFooterHeight);
        } else {
            return CGSizeMake(CGRectGetWidth(collectionView.frame), 13);
        }
    }
    return CGSizeZero;
}

#pragma mark- UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    //  `contentOffset.x` = `collectionView.contentOffset.x` + `collectionView.contentInset.left`
    //  `contentOffset.y` = `collectionView.contentOffset.y` + `collectionView.contentInset.top`
    _contentOffset = CGPointMake(scrollView.contentOffset.x + self.collectionView.contentInset.left, scrollView.contentOffset.y + self.collectionView.contentInset.top);
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
        [self.delegate scrollViewDidScroll:scrollView];
    }
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
        [self.delegate scrollViewWillBeginDragging:scrollView];
    }
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
        [self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
    }
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) {
        [self.delegate scrollViewWillBeginDecelerating:scrollView];
    }
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
        [self.delegate scrollViewDidEndDecelerating:scrollView];
    }
}

#pragma mark - ZBJCalendarWeekViewDelegate
- (void)calendarWeekView:(ZBJCalendarWeekView *)weekView configureWeekDayLabel:(UILabel *)dayLabel atWeekDay:(NSInteger)weekDay {
    if (self.dataSource && [self.dataSource respondsToSelector:@selector(calendarView:configureWeekDayLabel:atWeekDay:)]) {
        [self.dataSource calendarView:self configureWeekDayLabel:dayLabel atWeekDay:weekDay];
    }
}

#pragma mark - getters

- (ZBJCalendarWeekView *)weekView {
    if (!_weekView) {
        _weekView = [[ZBJCalendarWeekView alloc] init];
    }
    return _weekView;
}



- (UICollectionView *)collectionView {
    if (!_collectionView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumLineSpacing = 0;
        layout.minimumInteritemSpacing = 0;
        
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
        _collectionView.backgroundColor = [UIColor whiteColor];
        _collectionView.dataSource = self;
        _collectionView.delegate = self;
    }
    return _collectionView;
}

#pragma mark - setters
- (void)setSelectionMode:(ZBJSelectionMode)selectionMode {
    _selectionMode = selectionMode;
    switch (_selectionMode) {
        case ZBJSelectionModeSingle: {
            self.collectionView.allowsSelection = YES;
            self.collectionView.allowsMultipleSelection = NO;
            break;
        }
        case ZBJSelectionModeMutiple: {
            self.collectionView.allowsSelection = YES;
            self.collectionView.allowsMultipleSelection = YES;
            break;
        }
        default: {
            self.collectionView.allowsSelection = NO;
            self.collectionView.allowsMultipleSelection = NO;
            break;
        }
    }
}

@end
