import { _decorator, math } from 'cc';
import { YXFlowLayout } from '../lib';
import { YXCollectionView, YXEdgeInsets, YXIndexPath, YXLayoutAttributes } from '../lib';
const { ccclass, property } = _decorator;

/**
 * 用来实现突出选中节点效果的布局规则
 */
export class YXCoverLayout extends YXFlowLayout {

    /**
     * 禁止外部使用的父类属性
     */
    sectionInset: never
    getSectionInset(): never { return this.sectionInset }

    /**
     * 节点大小
     */
    itemSize: math.Size = null
    getItemSize(): math.Size { return this.itemSize }

    /**
     * 非选中节点的缩放系数
     */
    scaleValue: number = 0.8

    /**
     * 非选中节点的 x 轴旋转角度
     * 仅支持 3D 节点
     */
    angleX: number = 0

    /**
     * 非选中节点的 y 轴旋转角度
     * 仅支持 3D 节点
     */
    angleY: number = 0

    /**
     * 非选中节点的 z 轴旋转角度
     */
    angleZ: number = 0

    /**
     * 构造函数，此类布局必须要确定节点大小属性
     * @param itemSize 
     */
    constructor(itemSize: math.Size) {
        super()
        this.itemSize = itemSize
    }

    prepare(collectionView: YXCollectionView): void {
        let pad_horizontal = (collectionView.scrollView.view.width - this.itemSize.width) * 0.5
        let pad_vertical = (collectionView.scrollView.view.height - this.itemSize.height) * 0.5
        this.sectionInset = new YXEdgeInsets(pad_vertical, pad_horizontal, pad_vertical, pad_horizontal) as never
        super.prepare(collectionView)
    }

    targetOffset(collectionView: YXCollectionView, touchMoveVelocity: math.Vec3, startOffset: math.Vec2): { offset: math.Vec2; time: number; } {
        if (this.pagingEnabled == false) {
            return null
        }

        let offset = collectionView.scrollView.getScrollOffset()
        offset.x = - offset.x

        // 找出离屏幕中心最近的节点
        let visibleRect = new math.Rect()
        visibleRect.origin = collectionView.scrollView.getScrollOffset()
        visibleRect.x = - visibleRect.x
        visibleRect.size = collectionView.scrollView.view.contentSize
        let result = this.layoutAttributesForElementsInRect(visibleRect, collectionView)

        let target: YXLayoutAttributes = null
        if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
            let mid = offset.x + collectionView.scrollView.view.width * 0.5
            result.forEach((element) => {
                let distance1 = Math.abs(element.frame.center.x - mid)
                let distance2 = target ? Math.abs(target.frame.center.x - mid) : null
                if (distance2 == null || distance1 < distance2) {
                    target = element
                }
            })
            offset.x = target.frame.center.x - collectionView.scrollView.view.width * 0.5
        }
        if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
            let mid = offset.y + collectionView.scrollView.view.height * 0.5
            result.forEach((element) => {
                let distance1 = Math.abs(element.frame.center.y - mid)
                let distance2 = target ? Math.abs(target.frame.center.y - mid) : null
                if (distance2 == null || distance1 < distance2) {
                    target = element
                }
            })
            offset.y = target.frame.center.y - collectionView.scrollView.view.height * 0.5
        }
        return { offset: offset, time: 0.5 }
    }

    scrollTo(indexPath: YXIndexPath, collectionView: YXCollectionView): math.Vec2 {
        let attr = this.layoutAttributesForItemAtIndexPath(indexPath, collectionView)
        if (attr) {
            let offset = attr.frame.origin
            if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
                offset.x = offset.x - (collectionView.scrollView.view.width - attr.frame.width) * 0.5
            }
            if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
                offset.y = offset.y - (collectionView.scrollView.view.height - attr.frame.height) * 0.5
            }
            return offset
        }
        return null
    }

    layoutAttributesForElementsInRect(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
        let result = super.layoutAttributesForElementsInRect(rect, collectionView)
        let offset = collectionView.scrollView.getScrollOffset()
        offset.x = - offset.x

        let scale = this.scaleValue
        if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
            let mid = offset.x + collectionView.scrollView.view.width * 0.5
            result.forEach((element) => {
                let diff = element.frame.center.x - mid
                let distance = Math.abs(diff)
                let progress = distance / this.itemSize.width
                progress = Math.min(1, progress)
                let scaleValue = 1 - (1 - scale) * progress
                element.scale = new math.Vec3(scaleValue, scaleValue, 1)
                element.zIndex = -distance

                let eulerAngles = new math.Vec3()
                eulerAngles.x = progress * this.angleX * (diff > 0 ? -1 : 1)
                eulerAngles.y = progress * this.angleY * (diff > 0 ? -1 : 1)
                eulerAngles.z = progress * this.angleZ * (diff > 0 ? -1 : 1)
                element.eulerAngles = eulerAngles
            })
        }

        if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
            let mid = offset.y + collectionView.scrollView.view.height * 0.5
            result.forEach((element) => {
                let diff = element.frame.center.y - mid
                let distance = Math.abs(diff)
                let progress = distance / this.itemSize.height
                progress = Math.min(1, progress)
                let scaleValue = 1 - (1 - scale) * progress
                element.scale = new math.Vec3(scaleValue, scaleValue, 1)
                element.zIndex = 1 - progress

                let eulerAngles = new math.Vec3()
                eulerAngles.x = progress * this.angleX * (diff > 0 ? -1 : 1)
                eulerAngles.y = progress * this.angleY * (diff > 0 ? -1 : 1)
                eulerAngles.z = progress * this.angleZ * (diff > 0 ? -1 : 1)
                element.eulerAngles = eulerAngles
            })
        }

        return result
    }

    shouldUpdateAttributesZIndex(): boolean {
        return true
    }

    shouldUpdateAttributesForBoundsChange(): boolean {
        return true
    }
}