//
//  HDScrollJoinView.m
//  HDCollectionView_Example
//
//  Created by HaoDong chen on 2019/5/29.
//  Copyright © 2019 donggelaile. All rights reserved.
//

#import "HDScrollJoinView.h"
#import <objc/runtime.h>
static NSString *HDScrollJoinViewContentSize = @"contentSize";
static char *HDRelativeOriginalFrameKey;
static char *HDPrivateContentSizeKey;
static char *HDOriginalFrameKey;
static char *HDStopViewMaxXYKey;
@interface HDScrollJoinView()<UIScrollViewDelegate>
{
    CGPoint currentContentOffset;
    NSArray *currentStopViews;
}
@property (nonatomic, strong) NSMutableArray <id<HDScrollJoinViewRealScroll>> *innerListArr;
@property (nonatomic, assign) UICollectionViewScrollDirection scrollDirection;
@end
@implementation HDScrollJoinView

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.delegate = self;
    }
    return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.delegate = self;
    }
    return self;
}
- (NSMutableArray<id<HDScrollJoinViewRealScroll>> *)innerListArr
{
    if (!_innerListArr) {
        _innerListArr = @[].mutableCopy;
    }
    return _innerListArr;
}

- (void)hd_setListViews:(NSArray<id<HDScrollJoinViewRealScroll>> *)listViews scrollDirection:(UICollectionViewScrollDirection)scrollDirection
{
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    
    [listViews enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        BOOL isAdd = NO;
        if ([obj isKindOfClass:[UIView class]] &&
            [obj respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]&&
            [[obj hd_ScrollJoinViewRealScroll] isKindOfClass:[UIScrollView class]]) {
            
            UIScrollView *sc = [obj hd_ScrollJoinViewRealScroll];
            sc.showsVerticalScrollIndicator = NO;
            sc.showsHorizontalScrollIndicator = NO;
            sc.scrollEnabled = NO;
            [sc addObserver:self forKeyPath:HDScrollJoinViewContentSize options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(obj)];
            
            isAdd = YES;
        }else if ([obj isKindOfClass:[UIView class]] && [obj respondsToSelector:@selector(hd_subViewStopType)]){
            isAdd = YES;
        }

        if (isAdd) {
            [self .innerListArr addObject:obj];
            [self addSubview:(UIView*)obj];
            [self sendSubviewToBack:(UIView*)obj];
        }
    }];
    self.scrollDirection = scrollDirection;
    [self.superview layoutIfNeeded];
    [self hd_refreshAll];
}
- (void)hd_refreshAll
{
    CGFloat contentWidth = self.frame.size.width;
    CGFloat contentHeight = self.frame.size.height;
    
    __block CGFloat listContentWidthSum = 0;
    __block CGFloat listContentHeightSum = 0;
    
    __block CGFloat currentX = 0;
    __block CGFloat currentY = 0;
    //计算每个子view合适的大小及最终contentSize
    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIView *view = (UIView *)obj;
        CGSize hdSize = [objc_getAssociatedObject(obj, &HDPrivateContentSizeKey) CGSizeValue];
        if (CGSizeEqualToSize(CGSizeZero, hdSize)) {
            if ([obj respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]) {
                UIScrollView *sc = [obj hd_ScrollJoinViewRealScroll];
                hdSize = sc.contentSize;
            }else{
                hdSize = view.frame.size;
            }
        }
        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            
            CGFloat scFitH = hdSize.height>contentHeight?contentHeight:hdSize.height;
            view.frame = CGRectMake(currentX, currentY, contentWidth, scFitH);
            currentY = CGRectGetMaxY(view.frame);
            objc_setAssociatedObject(obj, &HDOriginalFrameKey, [NSValue valueWithCGRect:view.frame], OBJC_ASSOCIATION_RETAIN);
            
            //保存相对frame原始值
            CGRect orgFrame = view.frame;
            orgFrame.origin.y = listContentHeightSum;
            orgFrame.size.height = hdSize.height;
            objc_setAssociatedObject(obj, &HDRelativeOriginalFrameKey, [NSValue valueWithCGRect:orgFrame], OBJC_ASSOCIATION_RETAIN);

            listContentHeightSum += hdSize.height;
            
        }else{
            
            CGFloat scFitW = hdSize.width>contentWidth?contentWidth:hdSize.width;
            view.frame = CGRectMake(currentX, currentY, scFitW,contentHeight);
            currentX = CGRectGetMaxX(view.frame);
            objc_setAssociatedObject(obj, &HDOriginalFrameKey, [NSValue valueWithCGRect:view.frame], OBJC_ASSOCIATION_RETAIN);
            
            CGRect orgFrame = view.frame;
            orgFrame.origin.x = listContentWidthSum;
            orgFrame.size.width = hdSize.width;
            objc_setAssociatedObject(obj, &HDRelativeOriginalFrameKey, [NSValue valueWithCGRect:orgFrame], OBJC_ASSOCIATION_RETAIN);
            
            listContentWidthSum += hdSize.width;
        }
        [view setNeedsLayout];
        [view layoutIfNeeded];
    }];
    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIView *view = (UIView *)obj;
        if ([obj respondsToSelector:@selector(hd_subViewStopType)]) {
            if ([obj hd_subViewStopType] == HDScrollJoinViewStopTypeWhenNextDismiss) {
                //如果当前view悬停类型为HDScrollJoinViewStopTypeWhenNextDismiss时需要保存 其MAXY
                NSInteger nextIndex = idx + 1;
                CGFloat maxXY = 0;
                if (nextIndex>0 && nextIndex < self.innerListArr.count) {
                    UIView *nextView = (UIView*)self.innerListArr[nextIndex];
                    CGRect nextOrgFrame = [objc_getAssociatedObject(nextView, &HDRelativeOriginalFrameKey) CGRectValue];
                    if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
                        maxXY = CGRectGetMaxY(nextOrgFrame) - view.frame.size.height;
                        objc_setAssociatedObject(view, &HDStopViewMaxXYKey, @(maxXY), OBJC_ASSOCIATION_RETAIN);
                    }else{
                        maxXY = CGRectGetMaxX(nextOrgFrame) - view.frame.size.width;
                        objc_setAssociatedObject(view, &HDStopViewMaxXYKey, @(maxXY), OBJC_ASSOCIATION_RETAIN);
                    }
                }
            }
        }
    }];
    
    if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
        contentHeight = MAX(contentHeight, listContentHeightSum);
    }else{
        contentWidth = MAX(contentWidth, listContentWidthSum);
    }
    self.contentSize = CGSizeMake(contentWidth, contentHeight);
    [self scrollViewDidScroll:self];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    currentContentOffset = self.contentOffset;
    id<HDScrollJoinViewRealScroll> subMainview = [self findCurrentScrollingSubView];
    if (subMainview) {
        [self updateCurrentSc:subMainview];
    }
}
- (id<HDScrollJoinViewRealScroll>)findCurrentScrollingSubView
{
    //查找当前正在滑动展示哪个view
    CGFloat offsetXY = 0;
    if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
        offsetXY = currentContentOffset.y;
    }else{
        offsetXY = currentContentOffset.x;
    }
    __block id<HDScrollJoinViewRealScroll> currentView;
    //从前往后依次查找
    __block CGFloat offsetSum = 0;
    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CGSize fitSize = CGSizeZero;
        if ([obj respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]) {
            UIScrollView *sc = [obj hd_ScrollJoinViewRealScroll];
            fitSize = sc.contentSize;
        }else{
            fitSize = [(UIView*)obj frame].size;
        }
        
        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            offsetSum += fitSize.height;
            if (offsetXY < offsetSum ) {
                currentView = obj;
                *stop = YES;
            }
            
        }else{
            offsetSum += fitSize.width;
            if (offsetXY < offsetSum) {
                currentView = obj;
                *stop = YES;
            }
        }
    }];
    return currentView;
}
- (void)updateCurrentSc:(id<HDScrollJoinViewRealScroll>)subMainView
{
    if (!subMainView) {
        return;
    }
    [self updateStopViewsFrame:subMainView];
    
    UIView *view = (UIView *)subMainView;
    if ([subMainView respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]) {
        UIScrollView *currentSc = [subMainView hd_ScrollJoinViewRealScroll];
        CGFloat frameX = 0;
        CGFloat frameY = 0;
        CGFloat contentX = 0;
        CGFloat contentY = 0;
        
        CGRect frame = [objc_getAssociatedObject(view, &HDOriginalFrameKey) CGRectValue];
        
        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            CGRect relativeFrame = [objc_getAssociatedObject(view, &HDRelativeOriginalFrameKey) CGRectValue];
            contentY = currentContentOffset.y - relativeFrame.origin.y;
            //判断当前滑动view是否已经滑到底部
            CGFloat maxContentY = currentSc.contentSize.height - frame.size.height;
            if (contentY >= maxContentY) {
                frameY = CGRectGetMaxY(relativeFrame) - frame.size.height;
                contentY = maxContentY;
            }else{
                frameY = currentContentOffset.y;
            }
        }else{
            CGRect relativeFrame = [objc_getAssociatedObject(view, &HDRelativeOriginalFrameKey) CGRectValue];
            contentX = currentContentOffset.x - relativeFrame.origin.x;
            CGFloat maxContentX = currentSc.contentSize.width - frame.size.width;
            if (contentX >= maxContentX) {
                frameX = CGRectGetMaxX(relativeFrame) - frame.size.width;
                contentX = maxContentX;
            }else{
                frameX = currentContentOffset.x;
            }
        }
        view.frame = CGRectMake(frameX, frameY, view.frame.size.width, view.frame.size.height);
        currentSc.contentOffset = CGPointMake(contentX, contentY);
    }
    
    NSInteger curIndex = [self.innerListArr indexOfObject:subMainView];
    
    //其后的view保持在上个view的最 下/右 面
    UIView *frontView = (UIView *)subMainView;
    if (curIndex != NSNotFound) {
        for (NSInteger i = curIndex+1; i < self.innerListArr.count; i++) {
            UIView *behindView = (UIView*)self.innerListArr[i];
            CGRect frame = [objc_getAssociatedObject(behindView, &HDOriginalFrameKey) CGRectValue];
            if (![behindView respondsToSelector:@selector(hd_subViewStopType)]) {
                if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
                    frame.origin.y = CGRectGetMaxY([objc_getAssociatedObject(frontView, &HDRelativeOriginalFrameKey) CGRectValue]);
                }else{
                    frame.origin.x = CGRectGetMaxX([objc_getAssociatedObject(frontView, &HDRelativeOriginalFrameKey) CGRectValue]);
                }
                behindView.frame = frame;
            }
            if ([behindView respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]) {
                UIScrollView *sc = [(id<HDScrollJoinViewRealScroll>)behindView hd_ScrollJoinViewRealScroll];
                sc.contentOffset = CGPointZero;
            }
            frontView = behindView;
        }
    }
    
}
- (void)updateStopViewsFrame:(id<HDScrollJoinViewRealScroll>)currentScView
{
    //找到所有需要悬停的view
    NSMutableArray *needStopViewArr = @[].mutableCopy;
    NSInteger currentScIndex = [self.innerListArr indexOfObject:currentScView];
    __block NSInteger maxCurrentVisualVIndex = 0;
    if (currentScIndex != NSNotFound) {
        maxCurrentVisualVIndex = currentScIndex;
    }
    
    CGRect currentVisualRect = self.bounds;
    
    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CGRect reOrgFrame = [objc_getAssociatedObject(obj, &HDRelativeOriginalFrameKey) CGRectValue];
        if (CGRectIntersectsRect(currentVisualRect,reOrgFrame)) {
            if (idx>maxCurrentVisualVIndex) {
                maxCurrentVisualVIndex = idx;
            }
        }
    }];

    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj respondsToSelector:@selector(hd_subViewStopType)]) {
            [needStopViewArr addObject:obj];
        }
        if (idx == maxCurrentVisualVIndex) {
            *stop = YES;
        }
    }];
    
    __block CGFloat currentOffset = 0;
    [needStopViewArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIView *stopView = (UIView*)obj;
        HDScrollJoinViewStopType stopType = [obj hd_subViewStopType];
        CGRect frame = [objc_getAssociatedObject(stopView, &HDOriginalFrameKey) CGRectValue];
        
        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            CGFloat orgMinXY = CGRectGetMinY([objc_getAssociatedObject(stopView, &HDRelativeOriginalFrameKey) CGRectValue]);
            if (stopType == HDScrollJoinViewStopTypeAlways) {
                frame.origin.y = MAX(self->currentContentOffset.y + currentOffset, orgMinXY);
                stopView.frame = frame;
                currentOffset += frame.size.height;
            }else if (stopType == HDScrollJoinViewStopTypeWhenNextDismiss){
                CGFloat orgMaxXY = [objc_getAssociatedObject(stopView, &HDStopViewMaxXYKey) floatValue];
                CGFloat maxY = MAX(self->currentContentOffset.y + currentOffset, orgMinXY);
                frame.origin.y = MIN(orgMaxXY, maxY);
                stopView.frame = frame;
            }
        }else{
            CGFloat orgMinXY = CGRectGetMinX([objc_getAssociatedObject(stopView, &HDRelativeOriginalFrameKey) CGRectValue]);
            if (stopType == HDScrollJoinViewStopTypeAlways) {
                frame.origin.x = MAX(self->currentContentOffset.x + currentOffset, orgMinXY);
                stopView.frame = frame;
                currentOffset += frame.size.width;
            }else if (stopType == HDScrollJoinViewStopTypeWhenNextDismiss){
                CGFloat orgMaxXY = [objc_getAssociatedObject(stopView, &HDStopViewMaxXYKey) floatValue];
                CGFloat maxX = MAX(self->currentContentOffset.x + currentOffset, orgMinXY);
                frame.origin.x = MIN(orgMaxXY, maxX);
                stopView.frame = frame;
            }
        }
        

    }];
    currentStopViews = needStopViewArr;

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:HDScrollJoinViewContentSize]) {
        id<HDScrollJoinViewRealScroll> mainSubView = (__bridge id<HDScrollJoinViewRealScroll>)(context);
        CGSize oldSize = [objc_getAssociatedObject((__bridge id _Nonnull)(context), &HDPrivateContentSizeKey) CGSizeValue];
        __block CGSize newSize = [change[NSKeyValueChangeNewKey] CGSizeValue];
        
        if ([mainSubView respondsToSelector:@selector(hd_properContentSize:)]) {
            [mainSubView hd_properContentSize:^(CGSize properSize) {
                newSize = properSize;
                if (!CGSizeEqualToSize(oldSize, newSize)) {
                    objc_setAssociatedObject((__bridge id _Nonnull)(context), &HDPrivateContentSizeKey, [NSValue valueWithCGSize:newSize], OBJC_ASSOCIATION_RETAIN);
                    [self hd_refreshAll];
                }
            }];
        }else{
            if (!CGSizeEqualToSize(oldSize, newSize)) {
                objc_setAssociatedObject((__bridge id _Nonnull)(context), &HDPrivateContentSizeKey, [NSValue valueWithCGSize:newSize], OBJC_ASSOCIATION_RETAIN);
                [self hd_refreshAll];
            }
        }
        
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
- (void)dealloc
{
    [self.innerListArr enumerateObjectsUsingBlock:^(id<HDScrollJoinViewRealScroll>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj respondsToSelector:@selector(hd_ScrollJoinViewRealScroll)]) {
            UIScrollView *sc = [obj hd_ScrollJoinViewRealScroll];
            [sc removeObserver:self forKeyPath:HDScrollJoinViewContentSize];
        }
    }];
}
@end
