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

const _vec2Out = new math.Vec2()

/**
 * 旋转木马，会加载所有节点，适合少量数据
 * 仅支持了水平方向滚动
 */
export class YXCarouselLayout extends YXLayout {
    /**
     * 节点大小
     */
    itemSize: math.Size = null

    /**
     * 是否开启循环滚动
     */
    loop: boolean = true

    /**
     * 半径
     */
    radius: number = 300

    /**
     * 最远节点的缩放比例，取值范围 [ 0 ~ 1 ]
     * 模拟近大远小的视觉效果
     */
    minScale: number = 0.9

    /**
     * 最远节点的透明度变化，取值范围 [ 0 ~ 1 ]
     */
    minAlpha: number = null

    /**
     * 整体绕 x 轴旋转角度  
     * 备注: 只是影响节点的 y 坐标位置，并不会有 3d 旋转效果
     */
    angleX: number = 20

    prepare(collectionView: YXCollectionView): void {
        collectionView.scrollView.horizontal = true
        collectionView.scrollView.vertical = false
        let array = []
        let contentSize = collectionView.scrollView.view.contentSize.clone()
        let numberOfItems = collectionView.numberOfItems instanceof Function ? collectionView.numberOfItems(0, collectionView) : collectionView.numberOfItems
        for (let item = 0; item < numberOfItems; item++) {
            let indexPath = new YXIndexPath(0, item)
            let attr = new YXLayoutAttributes()
            attr.indexPath = indexPath
            // 此类布局主要布局业务是在 layoutAttributesForElementsInRect ，这里只是创建出来确定一下节点大小，去 layoutAttributesForElementsInRect 里实时的调整变换效果
            attr.frame = new math.Rect()
            attr.frame.size = this.itemSize
            array.push(attr)
        }
        this.attributes = array
        contentSize.width = contentSize.width * numberOfItems * (this.loop ? 3 : 1)
        this.contentSize = contentSize
    }

    layoutAttributesForElementsInRect(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
        // 获取当前滚动视图偏移位置
        let offset = rect.origin

        // 圆点
        let contentSize = collectionView.scrollView.view.contentSize.clone()
        let center = new math.Vec2(contentSize.width * 0.5, contentSize.height * 0.5)

        // 节点分布间隔
        let angleIncrement = 360 / this.attributes.length

        for (let index = 0; index < this.attributes.length; index++) {
            const element = this.attributes[index];
            // 当前节点角度
            let itemAngle = 360 - element.indexPath.item * angleIncrement
            // 从 90 度的位置开始排列
            itemAngle = itemAngle + 90
            // 根据当前滚动位置计算节点的偏移角度
            let angleOffset = (offset.x * 1) / contentSize.width * angleIncrement
            itemAngle = itemAngle + angleOffset
            // 将角度转换为弧度
            itemAngle = itemAngle % 360
            const angle = misc.degreesToRadians(itemAngle);
            // 计算节点变换
            let x = center.x + this.radius * Math.cos(angle);
            let z = 0 + this.radius * Math.sin(angle)
            let y = center.y + z * Math.sin(misc.degreesToRadians(this.angleX))
            _vec2Out.x = offset.x + x
            _vec2Out.y = y
            element.frame.center = _vec2Out
            element.zIndex = z
            // 通过缩放来补充近大远小的效果
            let progress = Math.floor(Math.sin(angle) * 10000) / 10000
            if (this.minScale) {
                let scaleValue = this.minScale + (1 - this.minScale) * progress
                element.scale = new math.Vec3(scaleValue, scaleValue, 1)
            }
            if (this.minAlpha) {
                let opacity = this.minAlpha + (1 - this.minAlpha) * progress
                element.opacity = opacity * 255
            }
        }

        return this.attributes
    }

    initOffset(collectionView: YXCollectionView): void {
        let x = (this.loop ? 1 : 0) * collectionView.scrollView.view.width * this.attributes.length
        _vec2Out.x = x
        _vec2Out.y = 0
        collectionView.scrollView.scrollToOffset(_vec2Out)
    }

    shouldUpdateAttributesZIndex(): boolean {
        return true
    }

    shouldUpdateAttributesOpacity(): boolean {
        return true
    }

    shouldUpdateAttributesForBoundsChange(): boolean {
        return true
    }

    scrollTo(indexPath: YXIndexPath, collectionView: YXCollectionView): math.Vec2 {
        let leftX = (this.attributes.length * 0 + indexPath.item) * collectionView.scrollView.view.width
        let midX = (this.attributes.length * 1 + indexPath.item) * collectionView.scrollView.view.width
        let rightX = (this.attributes.length * 2 + indexPath.item) * collectionView.scrollView.view.width
        let currentX = - collectionView.scrollView.getScrollOffset().x
        // 挑出移动距离最短的一个位置
        let array = [{ flag: 0, value: leftX }, { flag: 1, value: midX }, { flag: 2, value: rightX }]
        let flag = array.sort((a, b) => Math.abs(currentX - a.value) - Math.abs(currentX - b.value)).shift().flag
        do {
            if (flag == 0) { _vec2Out.x = leftX; break }
            if (flag == 1) { _vec2Out.x = midX; break }
            if (flag == 2) { _vec2Out.x = rightX; break }
        } while (false);
        _vec2Out.y = 0
        return _vec2Out
    }

    onScrollEnded(collectionView: YXCollectionView): void {
        if (this.loop) {
            let offset = collectionView.scrollView.getScrollOffset()
            offset.x = - offset.x
            let fullWidth = collectionView.scrollView.view.width * this.attributes.length
            let x = offset.x % fullWidth + fullWidth
            _vec2Out.x = x
            _vec2Out.y = 0
            collectionView.scrollView.scrollToOffset(_vec2Out)
            collectionView.markForUpdateVisibleData(true)
        }
    }
}

