/** 
 * @Author  wutianhao 
 * @ctime   2021-07-13
 * @Version 
 *      ScrollBeta1.0   2021-07-13 ScrollFinal改版 支持带标题的滚动列表 具体用法可参照天赋技能预览界面
 * @Tips
 *      复用滚动轴 用来减少drawcall
 *      与cc.ScrollView组件一同挂载在一个节点上
 *      item挂载的脚本必须添加setData方法，用来传递数据
 *      item锚点应该在中心
 *      目前Grid类型只支持从左上到右下模式(垂直滚动)，其他奇葩模式自己搞定
 *      滚动轴的锚点必须放在滚动列表的起始位置(比如背包grid模式在左上角，成就列表在左上角)
 */

import AssetMgr from "./AssetMgr";

// 对象池类型
let PoolEnum = cc.Enum({
    /**通用道具 */
    ITEM_BASE: 0,
    /**背包道具 */
    ITEM_BAG: 1,
    // /**合成道具 */
    // ITEM_COMPOSE: 2,
})

// 滚动类型
let ScrollDirEnum = cc.Enum({
    /**垂直*/
    VERTICAL: 0,
    /**水平*/
    HORIZONTAL: 1,
    /**背包*/
    GRID: 2
})

type ItemList = { index: number, node: cc.Node }

const { ccclass, property, menu } = cc._decorator;
@ccclass
@menu('Scroll/ScrollBeta')
export default class ScrollBeta extends cc.Component {
    // @property(cc.ScrollView)
    // scroll: cc.ScrollView | null = null
    @property({
        tooltip: "使用对象池"
    })
    useNodePool: boolean = false
    @property({
        type: PoolEnum,
        visible: function () { return this.useNodePool },
        tooltip: "对象池类型"
    })
    private poolType = PoolEnum.ITEM_BASE

    @property({
        type: ScrollDirEnum,
        visible: () => {
            return true
        },
        tooltip: "滚动类型"
    })
    private scrollDir = ScrollDirEnum.VERTICAL  // 滚动类型

    @property({ tooltip: "与滚动层的边界-左" })
    private padingX: number = 10
    @property({ tooltip: "与滚动层的边界-上" })
    private padingY: number = 10
    @property({ tooltip: "与滚动层的边界-下" })
    private padingY2: number = 0
    @property({
        visible: function () { return this.scrollDir != ScrollDirEnum.VERTICAL }, tooltip: "item行间距"
    })
    private spacingX: number = 20
    @property({
        visible: function () { return this.scrollDir != ScrollDirEnum.HORIZONTAL }, tooltip: "item列间距"
    })
    private spacingY: number = 20

    @property(cc.Prefab)
    itemPrefab: cc.Prefab = null   // item资源加载地址
    @property(cc.Prefab)
    itemPrefabTitle: cc.Prefab = null   // item标题资源加载地址
    @property
    itemScript: string = ""// item挂在的脚本名
    @property
    itemScriptTitle: string = ""// item标题挂在的脚本名
    @property
    itemScale: number = 1    // item缩放比例

    @property({
        tooltip: "是否开启滚动惯性"
    })
    inertia: boolean = true
    @property({
        tooltip: "开启惯性后,在用户停止触摸后滚动多块停止,0表示永不停止,1表示立即停止",
        visible: function () { return this.inertia == true },
    })
    brake: number = 0.75
    @property({
        tooltip: "是否允许滚动内容超过边界,并在停止触摸后回弹"
    })
    elastic: boolean = true
    @property({
        tooltip: "滚动行为是否会取消子节点上注册的触摸事件",
    })
    cancelInnerEvents: boolean = true

    @property({
        tooltip: "展示生产动画\n0->不展示\n1->缩放动画"
    })
    showAnim: number = 1

    @property({
        tooltip: "创建item的延迟时间,设为0则为每帧生成。\n注意:在所有item刷新完之前,scroll组件的滚动功能将被关闭"
    })
    ctime: number = 0
    @property({
        tooltip: "每帧生成item的个数"
    })
    cnumber: number = 1

    scrollView: cc.ScrollView
    mask: cc.Node
    content: cc.Node



    itemDataList: any[] = []   // 当前显示阵营的所有数据
    itemDataListMain: any[] = []   // 当前显示主数据的所有数据
    itemDataListTitle: any[] = []   // 当前显示标题的所有数据
    private extraParams: any[] = []     // 额外数据
    private itemListMain: ItemList[] = []   // 实例化的item列表
    private itemListTitle: ItemList[] = []   // 实例化的item标题列表

    private instantiateCount: number = 0    // item实例化数量
    private instantiateCountTitle: number = 0    // item标题实例化数量
    private hangCount: number = 0           // 行个数
    private lieCount: number = 0            // 列个数
    private itemDistanceX: number = 0       // item中心点之间的距离
    private itemDistanceXTitle: number = 0       // item标题中心点之间的距离
    private itemDistanceY: number = 0       // item中心点之间的距离
    private itemDistanceYTitle: number = 0       // item标题中心点之间的距离
    private scrollMaxOffsetX: number = 0    // 最大可滚动区域X
    private scrollMaxOffsetY: number = 0    // 最大可滚动区域Y

    private lastScrollPos: number = 0       //上一次的滚动位置
    private curScrollPos: number = 0        // 当前滚动位置
    private isScrollUp: boolean = false       // 当前往哪个方向滚动   左和上是true

    private itemWidth: number = 10  // item宽度
    private itemWidthTitle: number = 10  // item标题宽度
    private itemHeight: number = 10 // item高度
    private itemHeightTitle: number = 10 // item标题高度

    private tagLang: number = 0
    private tagIndex: number = -999 // 999 表示清0状态，此时无【插入标签】,-1表示标签置顶，其他即当前标签的下方(右侧)显示

    private canCreateItem: boolean = false  // 可以生成item
    private createIndex: number = 0 // 生成item的数据标签
    private life: number = 0 // 生成item的时间
    private baseIndex: number = 0 // 基础标签位置

    private hasInit: boolean = false
    offset: number = 0 // 偏移量

    onLoad() {
        if (this.node.getComponent(cc.ScrollView) != null) {
            console.error("滚动节点无需挂载scrollView组件了")
            return
        }
        /////////////// 构建滚动轴 ///////////////
        this.scrollView = this.addComponent(cc.ScrollView)
        this.scrollView.horizontal = this.scrollDir == ScrollDirEnum.HORIZONTAL
        this.scrollView.vertical = this.scrollDir != ScrollDirEnum.HORIZONTAL
        this.scrollView.inertia = this.inertia
        this.scrollView.brake = this.brake
        this.scrollView.elastic = this.elastic
        this.scrollView.cancelInnerEvents = this.cancelInnerEvents
        /////////////// 构建滚动遮罩 ///////////////
        this.mask = new cc.Node()
        this.mask.parent = this.node
        this.mask.setContentSize(this.node.getContentSize())
        this.mask.addComponent(cc.Mask)
        this.mask.getComponent(cc.Mask).type = cc.Mask.Type.RECT
        this.mask.anchorX = this.node.anchorX
        this.mask.anchorY = this.node.anchorY
        this.mask.x = 0
        this.mask.y = 0
        // widget不需要加了
        // let maskWidget = mask.addComponent(cc.Widget)
        // maskWidget.isAlignTop = true
        // maskWidget.isAlignBottom = true
        // maskWidget.isAlignLeft = true
        // maskWidget.isAlignRight = true
        // maskWidget.top = 0
        // maskWidget.bottom = 0
        // maskWidget.left = 0
        // maskWidget.right = 0
        /////////////// 构建滚动内容器 ///////////////
        this.content = new cc.Node()
        this.content.parent = this.mask
        this.scrollView.content = this.content
        this.content.setContentSize(this.node.getContentSize())
        this.content.anchorX = this.node.anchorX
        this.content.anchorY = this.node.anchorY
        this.content.x = 0
        this.content.y = 0
    }

    resetSize() {
        if (this.mask) {
            this.mask.setContentSize(this.node.getContentSize())
        }
        if (this.content) {
            this.content.setContentSize(this.node.getContentSize())
        }
    }

    private init() {
        if (this.hasInit) { return }

        this.hasInit = true


        this.itemWidth = this.itemPrefab.data.getContentSize().width
        this.itemHeight = this.itemPrefab.data.getContentSize().height
        this.itemWidthTitle = this.itemPrefabTitle.data.getContentSize().width
        this.itemHeightTitle = this.itemPrefabTitle.data.getContentSize().height
        this.itemDistanceX = this.realItemWidth + this.spacingX
        this.itemDistanceY = this.realItemHeight + this.spacingY
        this.itemDistanceXTitle = this.itemWidthTitle + this.spacingX
        this.itemDistanceYTitle = this.itemHeightTitle + this.spacingY

        this.scrollView.node.on('scrolling', this.onScroll, this)

        //看下有没有不应该有的组件
        // Layout
        if (this.scrollView.content.getComponent(cc.Layout)) {
            console.error("scrollFinal 与 layout 冲突,清删除 content 中的 layout 组件")
        }
        // Widget
        if (this.scrollView.node.getComponent(cc.Widget)) {
            if (this.scrollView.node.getComponent(cc.Widget).isAlignTop &&
                this.scrollView.node.getComponent(cc.Widget).isAlignBottom) {
                console.error("不能用widget做长度适配(因为Widget的延迟),只可用作坐标适配")
            }
        }
    }
    private setInstantCount() {
        if (this.scrollView == null) {
            return
        }
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                this.hangCount = 1
                this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
                this.instantiateCount = this.lieCount
                break
            case ScrollDirEnum.HORIZONTAL:
                this.hangCount = Math.ceil(this.scrollView.node.width / (this.itemDistanceX)) + 1
                this.lieCount = 1
                this.instantiateCount = this.hangCount
                break
            case ScrollDirEnum.GRID:
                this.hangCount = Math.floor(this.scrollView.node.width / (this.itemDistanceX))
                this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
                this.instantiateCount = this.hangCount * this.lieCount
                break
        }
        this.instantiateCountTitle = this.itemDataListTitle.length
    }

    indexFrom: { [index: number]: number } = {}

    initScrollView(itemDataList: any[] = [], ...args: any[]) {
        // this.scheduleOnce(() => {
        this.init()
        this.clear()
        this.clearTag()
        this.itemDataList = itemDataList
        this.refreshData()
        this.extraParams = args
        this.scrollView.stopAutoScroll()
        this.setInstantCount()
        this.showUI()
        // }, 0)
    }

    refreshData() {
        this.itemDataListMain = []
        this.itemDataListTitle = []
        this.indexFrom = {}
        let count = 0
        this.itemDataList.forEach((element, index) => {
            element[0].forEach(_element => {
                this.itemDataListMain.push(_element)
            });
            this.itemDataListTitle.push(element[1])
            count += element[0].length
            this.indexFrom[index] = count
        });
    }

    private getPositionInView(item: cc.Node): { x: number, y: number } {
        if (this.scrollView == null) { return cc.v2(0, 0) }
        let worldPos = item.parent.convertToWorldSpaceAR(item.position);
        let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
        return viewPos;
    }

    private onScroll() {
        this.curScrollPos = 0
        if (this.scrollDir == ScrollDirEnum.HORIZONTAL) {
            this.curScrollPos = this.scrollView.getScrollOffset().x
            this.isScrollUp = this.curScrollPos < this.lastScrollPos
        } else {
            this.curScrollPos = this.scrollView.getScrollOffset().y
            this.isScrollUp = this.curScrollPos > this.lastScrollPos
        }
        this.lastScrollPos = this.curScrollPos

        if (this.scrollView == null) { return }

        this.itemListMain.forEach(ele => {
            this.tryResetItem(ele)
        })
        // this.itemListTitle.forEach(ele => {
        //     this.tryResetItemTitle(ele)
        // })
    }

    private tryResetItem(ele: ItemList) {
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                if (this.curScrollPos >= 0 || this.curScrollPos <= this.scrollMaxOffsetX) {
                    return
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                if (this.curScrollPos <= -this.realItemHeight / 2 || this.curScrollPos >= this.scrollMaxOffsetY + this.realItemHeight / 2) {
                    return
                }
        }
        let scrollWidth = this.scrollView.node.width
        let scrollHeight = this.scrollView.node.height
        let element = ele.node
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                if (this.isScrollUp && this.getPositionInView(element).x < -(this.itemDistanceX / 2)) {
                    // 超出左边界显示区域
                    let idx = ele.index + this.instantiateCount
                    if (idx < this.itemDataListMain.length) {
                        this.setItemData(element, this.itemDataListMain[idx], idx)
                        // element.x = element.x + this.hangCount * this.itemDistanceX
                        ele.index = idx
                        this.setPosX(idx, element)
                    }
                } else if (!this.isScrollUp && this.getPositionInView(element).x > (scrollWidth + this.itemDistanceX / 2)) {
                    // 超出右边界显示区域
                    let idx = ele.index - this.instantiateCount
                    if (idx >= 0) {
                        this.setItemData(element, this.itemDataListMain[idx], idx)
                        // element.x = element.x - this.hangCount * this.itemDistanceX
                        ele.index = idx
                        this.setPosX(idx, element)
                    }
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                if (this.isScrollUp && this.getPositionInView(element).y > this.itemDistanceY / 2) {
                    // 超出上边界显示区域
                    let idx = ele.index + this.instantiateCount
                    if (idx < this.itemDataListMain.length && this.isScrollUp) {
                        this.setItemData(element, this.itemDataListMain[idx], idx)
                        ele.index = idx
                        this.setPosX(idx, element)
                        this.setPosY(idx, element)
                    }
                } else if (!this.isScrollUp && this.curScrollPos > -this.itemDistanceY / 2 && this.getPositionInView(element).y < -(scrollHeight + this.itemDistanceY / 2)) {
                    // 超出下边界显示区域
                    let idx = ele.index - this.instantiateCount
                    if (idx >= 0 && this.isScrollUp == false) {
                        this.setItemData(element, this.itemDataListMain[idx], idx)
                        ele.index = idx
                        this.setPosX(idx, element)
                        this.setPosY(idx, element)
                    }
                }
                break
        }
    }

    private tryResetItemTitle(ele: ItemList) {
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                if (this.curScrollPos >= 0 || this.curScrollPos <= this.scrollMaxOffsetX) {
                    return
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                if (this.curScrollPos <= -this.itemHeightTitle / 2 || this.curScrollPos >= this.scrollMaxOffsetY + this.itemHeightTitle / 2) {
                    return
                }
        }
        let scrollWidth = this.scrollView.node.width
        let scrollHeight = this.scrollView.node.height
        let element = ele.node
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                if (this.isScrollUp && this.getPositionInView(element).x < -(this.itemDistanceXTitle / 2)) {
                    // 超出左边界显示区域
                    let idx = ele.index + this.instantiateCountTitle
                    if (idx < this.itemDataListTitle.length) {
                        this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
                    }
                } else if (!this.isScrollUp && this.getPositionInView(element).x > (scrollWidth + this.itemDistanceX / 2)) {
                    // 超出右边界显示区域
                    let idx = ele.index - this.instantiateCountTitle
                    if (idx >= 0) {
                        this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
                    }
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                if (this.isScrollUp && this.getPositionInView(element).y > this.itemDistanceYTitle / 2) {
                    // 超出上边界显示区域
                    let idx = ele.index + this.instantiateCountTitle
                    if (idx < this.itemDataListTitle.length && this.isScrollUp) {
                        this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
                    }
                } else if (!this.isScrollUp && this.curScrollPos > -this.itemDistanceYTitle / 2 && this.getPositionInView(element).y < -(scrollHeight + this.itemDistanceYTitle / 2)) {
                    // 超出下边界显示区域
                    let idx = ele.index - this.instantiateCountTitle
                    if (idx >= 0 && this.isScrollUp == false) {
                        this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
                    }
                }
                break
        }
    }


    checkIndexFromItem(index: number): number {
        let _index = 0
        for (let i = 0; i < index; i++) {
            const element = this.itemDataList[i][0]
            _index += element.length
        }
        return _index
    }

    checkIndexFromTitle(index: number): number {
        for (let i = 0; i < this.itemDataList.length; i++) {
            if (index < this.indexFrom[i]) {
                return i
            }
        }
    }

    checkAddTitle(index: number): boolean {
        let from = this.checkIndexFromTitle(index)
        if (from > 0) {
            return index == this.indexFrom[from - 1]
        } else {
            return index == 0
        }
    }

    private setPosX(index: number, node?: cc.Node): number {
        let x = 0
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
                break
            case ScrollDirEnum.HORIZONTAL:
                x = index * this.itemDistanceX + this.realItemWidth / 2 + this.padingX + ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceXTitle)
                if (this.tagIndex >= -1 && index > this.tagIndex) {
                    x += this.tagLang
                }
                break
            case ScrollDirEnum.GRID:
                let _index = this.checkIndexFromTitle(index)
                let count = _index > 0 ? this.indexFrom[_index - 1] : 0
                x = (index - count) % this.hangCount * this.itemDistanceX + this.realItemWidth / 2 + this.padingX
                break
        }
        if (node) {
            node.x = x
        }
        return x
    }
    private setPosY(index: number, node?: cc.Node): number {
        let y = 0
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                y = -index * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
                    ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceYTitle) + this.offset
                if (this.tagIndex >= -1 && index > this.tagIndex) {
                    y -= this.tagLang
                }
                break
            case ScrollDirEnum.HORIZONTAL:
                y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
                break
            case ScrollDirEnum.GRID:
                let _index = this.checkIndexFromTitle(index)
                let extra = 0
                if (_index > 0) {
                    for (let i = 0; i < _index; i++) {
                        extra += Math.ceil(this.itemDataList[i][0].length / this.hangCount)
                    }
                }
                let count = _index > 0 ? this.indexFrom[_index - 1] : 0
                y = -(Math.floor((index - count) / this.hangCount) + extra) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
                    ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceYTitle) + this.offset
                if (this.tagIndex >= -1 && (Math.floor((index - count) / this.hangCount) + extra) > this.tagIndex) {
                    y -= this.tagLang
                }
                break
        }
        if (node) {
            node.y = y
        }
        return y
    }


    private setPosXTitle(index: number, node?: cc.Node): number {
        let x = 0
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
                break
            case ScrollDirEnum.HORIZONTAL:
                x = this.checkIndexFromItem(index) * this.itemDistanceX + this.realItemWidth / 2 + this.padingX +
                    ((index + 1) * this.itemDistanceXTitle) - this.realItemWidth / 2 - this.spacingX - this.itemWidthTitle / 2
                if (this.tagIndex >= -1 && index > this.tagIndex) {
                    x += this.tagLang
                }
                break
            case ScrollDirEnum.GRID:
                x = this.scrollView.content.width / 2
                break
        }
        if (node) {
            node.x = x
        }
        return x
    }
    private setPosYTitle(index: number, node?: cc.Node): number {
        let y = 0
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                y = -this.checkIndexFromItem(index) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
                    ((index + 1) * this.itemDistanceYTitle) + this.realItemHeight / 2 + this.spacingY + this.itemHeightTitle / 2
                break
            case ScrollDirEnum.HORIZONTAL:
                y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
                break
            case ScrollDirEnum.GRID:
                let extra = 0
                if (index > 0) {
                    for (let i = 0; i < index; i++) {
                        extra += Math.ceil(this.itemDataList[i][0].length / this.hangCount)
                    }
                }
                let count = index > 0 ? this.indexFrom[index - 1] : 0
                y = -(Math.floor((this.checkIndexFromItem(index) - count) / this.hangCount) + extra) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
                    ((index + 1) * this.itemDistanceYTitle) + this.realItemHeight / 2 + this.spacingY + this.itemHeightTitle / 2
                if (this.tagIndex >= -1 && (Math.floor((index - count) / this.hangCount) + extra) > this.tagIndex) {
                    y -= this.tagLang
                }
                break
        }
        if (node) {
            node.y = y
        }
        return y
    }

    private setItemData(itemNode: cc.Node, data: any, index: number, isRefresh: boolean = false) {
        try {
            itemNode.getComponent(this.itemScript).setData(data, index, this.extraParams)
            if (isRefresh == false) {
                let addTitle = this.checkAddTitle(index)
                if (addTitle) {
                    this.addItemNodeTitle(this.checkIndexFromTitle(index), this.itemDataListTitle[this.checkIndexFromTitle(index)])
                }
            }
        } catch (error) {
            console.error("脚本中缺少setData方法，或者方法报错", error)
        }
    }
    private setItemDataTitle(itemNode: cc.Node, data: any, index: number) {
        try {
            itemNode.getComponent(this.itemScriptTitle).setData(data, index, this.extraParams)
        } catch (error) {
            console.error("脚本中缺少setData方法，或者方法报错", error)
        }
    }

    // refreshItems调用，在刷新时可能需要重置item的index标签
    private resetIndex(index: number): number {
        if (this.itemDataListMain[index] != null) {
            return index
        }
        return this.resetIndex(index - this.instantiateCount)
    }

    // 刷新单独的item
    refreshItem(index: number, data) {
        if (this.itemDataListMain[index] == null) {
            return
        }
        this.itemDataListMain[index] = data
        if (this.getItem(index) == null) {
            return
        }
        this.setItemData(this.getItem(index), this.itemDataListMain[index], index)
    }
    // refreshItem(index: number) {
    //     this.setItemData(this.getItem(index), this.itemDataListMain[index], index)
    // }
    refreshItems(itemDataList: any[], ...args: any[]) {
        this.itemDataList = itemDataList
        this.refreshData()
        if (args.length > 0) {
            this.extraParams = args
        }
        this.fixItemNodes()
        // 最终构造完整的 itemList 列表,刷新数据
        this.itemListMain.forEach(element => {
            try {
                let newIndex = this.resetIndex(element.index)
                element.index = newIndex
                this.setItemData(element.node, this.itemDataListMain[element.index], element.index)
                this.setPosX(element.index, element.node)
                this.setPosY(element.index, element.node)
                // element.node.getComponent(this.itemScript).setData(this.itemDataListMain[element.index], element.index, this.extraParams)
            } catch (error) {
                console.warn("脚本中缺少refreshItem方法，或者方法报错", error)
            }
        })
    }

    // 数据新增或减少时,增加或减少item
    fixItemNodes() {
        // 判断是否需要删除 itemList 里的数据
        if (this.itemDataListMain.length < this.instantiateCount && this.itemListMain.length > this.itemDataListMain.length) {
            let needDeleteCount = this.itemListMain.length - this.itemDataListMain.length
            for (let index = this.itemDataListMain.length; index < this.itemDataListMain.length + needDeleteCount; index++) {
                this.itemListMain[index].node.destroy()
            }
            this.itemListMain.splice(this.itemDataListMain.length, needDeleteCount)
            // 判断是否需要增加 itemList 里的数据
        } else if (this.itemListMain.length < this.instantiateCount && this.itemListMain.length < this.itemDataListMain.length) {
            let addCount = Math.min(this.instantiateCount - this.itemListMain.length, this.itemDataListMain.length - this.itemListMain.length)
            let startIndex = 0
            this.itemListMain.forEach(element => {
                startIndex = Math.max(element.index + 1, startIndex)
            });
            for (let addIndex = startIndex; addIndex < (startIndex + addCount); addIndex++) {
                this.addItemNode(addIndex, this.itemDataListMain[addIndex], true)
            }
        }
        // 判断是否需要删除 itemList 里的数据
        if (this.itemDataListTitle.length < this.instantiateCountTitle && this.itemListTitle.length > this.itemDataListTitle.length) {
            let needDeleteCount = this.itemListTitle.length - this.itemDataListTitle.length
            for (let index = this.itemDataListTitle.length; index < this.itemDataListTitle.length + needDeleteCount; index++) {
                this.itemListTitle[index].node.destroy()
            }
            this.itemListTitle.splice(this.itemDataListTitle.length, needDeleteCount)
        }
        this.instantiateCountTitle = this.itemDataListTitle.length
        if (this.content) {
            this.setScrollContentSize()
        }
    }

    private get realItemWidth(): number {
        return this.itemWidth * this.itemScale
    }

    private get realItemHeight(): number {
        return this.itemHeight * this.itemScale
    }

    private initItemNode(): cc.Node {
        return AssetMgr.instantiate(this.node, this.itemPrefab, false)
        // @TODO PoolManager
        // if (this.useNodePool) {
        //     if (this.poolType == PoolEnum.ITEM_BAG) {
        //         return PoolManager.getItemBag(this.itemPrefab)
        //     } else if (this.poolType == PoolEnum.ITEM_BASE) {
        //         return PoolManager.getItemBase(this.itemPrefab)
        //     }
        // } else {
        //     return cc.instantiate(this.itemPrefab)
        // }
    }

    private _initItemNode(): cc.Node {
        return AssetMgr.instantiate(this.node, this.itemPrefabTitle, false)
        // @TODO PoolManager
        // if (this.useNodePool) {
        //     if (this.poolType == PoolEnum.ITEM_BAG) {
        //         return PoolManager.getItemBag(this.itemPrefab)
        //     } else if (this.poolType == PoolEnum.ITEM_BASE) {
        //         return PoolManager.getItemBase(this.itemPrefab)
        //     }
        // } else {
        //     return cc.instantiate(this.itemPrefab)
        // }
    }

    private showUI() {
        if (this.itemPrefab == null || this.itemPrefabTitle == null) {
            console.error("item预制体加载失败")
            return
        }
        if (this.scrollView == null) {
            console.error("没有绑定scroll")
            return
        }

        this.setCreateItems(true)
        this.scrollView.content.setAnchorPoint(0, 1)
        this.scrollView.content.setPosition(-this.scrollView.node.width / 2, this.scrollView.node.height / 2)

        this.setScrollContentSize()
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                this.scrollView.scrollToTop()
                break
            case ScrollDirEnum.HORIZONTAL:
                this.scrollView.scrollToLeft()
                break
            case ScrollDirEnum.GRID:
                this.scrollView.scrollToTop()
                break
        }
    }

    private setCreateItems(bool: boolean) {
        if (bool) {
            this.scrollView.enabled = false
            this.canCreateItem = true
            this.createIndex = 0
            this.baseIndex = 0
            this.life = 0
            // this._canCreateItem = true
            // this._createIndex = 0
            // this._life = 0
        } else {
            this.scrollView.enabled = true
            this.canCreateItem = false
            this.createIndex = 0
            this.baseIndex = 0
            this.life = 0
            // this._canCreateItem = false
            // this._createIndex = 0
            // this._life = 0
        }
    }

    update(dt: number) {
        if (!this.canCreateItem) {
            return
        }
        for (let index = 0; index < this.cnumber; index++) {
            this.updateForCreateItem(dt)
        }
        // if (!this._canCreateItem) {
        //     return
        // }
        // for (let index = 0; index < this.cnumber; index++) {
        //     this._updateForCreateItem(dt)
        // }
    }
    private updateForCreateItem(dt: number) {
        if (!this.canCreateItem) {
            return
        }
        if (this.life == 0) {
            // 生
            let _itemData = this.itemDataListMain[this.baseIndex]
            if (this.createIndex >= this.instantiateCount || _itemData == null) {
                this.setCreateItems(false)
                return
            }
            this.addItemNode(this.baseIndex, _itemData)
            this.createIndex += 1
            this.baseIndex += 1
        }
        this.life += dt
        if (this.life >= this.ctime) {
            this.life = 0
        }
    }
    private setScrollContentSize() {
        let val = 0
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
                for (let i = 0; i < this.itemDataList.length; i++) {
                    val += this.itemDistanceYTitle
                    for (let j = 0; j < this.itemDataList[i][0].length; j++) {
                        val += this.itemDistanceY
                    }
                }
                this.scrollView.content.setContentSize(this.scrollView.node.width, val + this.padingY + this.padingY2 + this.tagLang)
                break
            case ScrollDirEnum.HORIZONTAL:
                for (let i = 0; i < this.itemDataList.length; i++) {
                    val += this.itemDistanceXTitle
                    for (let j = 0; j < this.itemDataList[i][0].length; j++) {
                        val += this.itemDistanceX
                    }
                }
                this.scrollView.content.setContentSize(val + this.padingX + this.tagLang, this.scrollView.node.height)
                break
            case ScrollDirEnum.GRID:
                for (let i = 0; i < this.itemDataList.length; i++) {
                    val += this.itemDistanceYTitle
                    for (let j = 0; j < Math.ceil(this.itemDataList[i][0].length / this.hangCount); j++) {
                        val += this.itemDistanceY
                    }
                }
                this.scrollView.content.setContentSize(this.scrollView.node.width, val + this.padingY + this.padingY2 + this.tagLang)
                break
        }
        this.scrollMaxOffsetX = -this.scrollView.getMaxScrollOffset().x
        this.scrollMaxOffsetY = this.scrollView.getMaxScrollOffset().y
    }
    /**
     * 弹出详情标签，将会重设后续item的坐标。
     * @param index item标签
     * @param lang 坐标偏移量
     */
    setTag(index: number, lang: number) {
        this.tagLang = lang
        switch (this.scrollDir) {
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.HORIZONTAL:
                this.tagIndex = index
                break
            case ScrollDirEnum.GRID:
                this.tagIndex = Math.floor(index / this.hangCount)
                break
        }
        this.setScrollContentSize()
        this.itemListMain.forEach((element) => {
            this.setPosX(element.index, element.node)
            this.setPosY(element.index, element.node)
        });
    }
    /**清除详情标签，恢复item默认坐标 */
    clearTag() {
        this.setTag(-999, 0)
        // 修正index和坐标
        let scrollHeight = this.scrollView.node.height
        this.itemListMain.forEach(ele => {
            // 判断是否超出边界
            if (this.getPositionInView(ele.node).y > this.itemDistanceY / 2) {
                if (ele.index + this.instantiateCount < this.itemDataListMain.length) {
                    ele.index += this.instantiateCount
                }
                this.setPosY(ele.index, ele.node)
                this.setItemData(ele.node, this.itemDataListMain[ele.index], ele.index)
            } else if (this.getPositionInView(ele.node).y < -(scrollHeight + this.itemDistanceY / 2)) {
                if (ele.index - this.instantiateCount >= 0) {
                    ele.index -= this.instantiateCount
                }
                this.setPosY(ele.index, ele.node)
                this.setItemData(ele.node, this.itemDataListMain[ele.index], ele.index)
            }
        })
        this.setScrollContentSize()
    }
    /**删除某个元素 */
    del(index: number) {
        if (this.itemDataListMain.length < index) { return }
        this.itemDataListMain.splice(index, 1)
        // // 判断下是否需要删除节点
        if (this.itemListMain.length > this.itemDataListMain.length) {
            this.itemListMain.pop().node.destroy()
        }
        this.setScrollContentSize()
        for (let index = 0; index < this.itemListMain.length; index++) {
            let element = this.itemListMain[index]
            if (this.itemDataListMain[element.index] == null) {
                element.index -= this.instantiateCount
                break
            }
        }
        this.itemListMain.forEach((element) => {
            this.setPosX(element.index, element.node)
            this.setPosY(element.index, element.node)
            this.setItemData(element.node, this.itemDataListMain[element.index], element.index)
        });
    }
    /**在末尾添加一个元素 */
    add(data: any) {
        this.itemDataListMain.push(data)
        // 判断是否需要增加item
        if (this.itemListMain.length >= this.instantiateCount) {
            // 不需要
            // 判断下是否需要把最前面的放到最后面:1
            let needUpdate = false
            for (let index = 0; index < this.itemListMain.length; index++) {
                if (this.itemListMain[index].index == this.itemDataListMain.length - 2) {
                    needUpdate = true
                    break
                }
            }
            if (needUpdate) {
                let minIndex = 1000 //这个是最小值
                let minUpdateIndex = 0// 这个是最小值的标签
                for (let i = 0; i < this.itemListMain.length; i++) {
                    if (this.itemListMain[i].index < minIndex) {
                        minIndex = this.itemListMain[i].index
                        minUpdateIndex = i
                    }
                }
                // 判断下是否需要把最前面的放到最后面:2
                let _nodePos = 0
                let _offset = 0
                let needUpdate2 = false
                switch (this.scrollDir) {
                    case ScrollDirEnum.HORIZONTAL:
                        _nodePos = this.itemListMain[minUpdateIndex].node.x
                        _offset = this.scrollView.getScrollOffset().x
                        needUpdate2 = _nodePos + _offset > this.itemWidth / 2
                        break
                    case ScrollDirEnum.VERTICAL:
                    case ScrollDirEnum.GRID:
                        _nodePos = this.itemListMain[minUpdateIndex].node.y
                        _offset = this.scrollView.getScrollOffset().y
                        needUpdate2 = _nodePos + _offset > this.itemHeight / 2
                        break
                }
                if (needUpdate2) {
                    this.itemListMain[minUpdateIndex].index += this.instantiateCount
                    this.setItemData(this.itemListMain[minUpdateIndex].node, data, this.itemListMain[minUpdateIndex].index)
                    this.setPosX(this.itemListMain[minUpdateIndex].index, this.itemListMain[minUpdateIndex].node)
                    this.setPosY(this.itemListMain[minUpdateIndex].index, this.itemListMain[minUpdateIndex].node)
                }
            }
        } else {
            // 需要
            let index = this.itemDataListMain.length - 1
            this.addItemNode(index, data)
        }
        this.setScrollContentSize()
    }

    private addItemNode(index: number, data: any, isRefresh: boolean = false) {
        let _node = this.initItemNode()
        this.scrollView.content.addChild(_node)
        this.setItemData(_node, data, index, isRefresh)
        this.itemListMain.push({ index: index, node: _node })
        this.setPosX(index, _node)
        this.setPosY(index, _node)
        if (isRefresh || this.showAnim == 0) {
            _node.scale = this.itemScale
        } else if (this.showAnim == 1) {
            _node.scale = 0
            cc.tween(_node).
                to(0.1, { scale: this.itemScale + 0.1 }).
                to(0.1, { scale: this.itemScale }).
                start()
        } else {
            _node.scale = this.itemScale
        }
    }

    private addItemNodeTitle(index: number, data: any) {
        if (this.itemListTitle.findIndex(n => n.index == index) == -1) {
            let _node = this._initItemNode()
            this.scrollView.content.addChild(_node)
            this.setItemDataTitle(_node, data, index)
            this.itemListTitle.push({ index: index, node: _node })
            _node.zIndex = -999
            this.setPosXTitle(index, _node)
            this.setPosYTitle(index, _node)
        } else {
            this.setItemDataTitle(this.itemListTitle[index].node, data, index)
            this.setPosXTitle(index, this.itemListTitle[index].node)
            this.setPosYTitle(index, this.itemListTitle[index].node)
        }
    }

    /**
     * @param val 立刻滚动到目标标签,目标标签将在顶部|左侧出现
     * @param type  滚到哪里的类型(1.居中 2.顶部|左侧 3.底部｜右侧)
     */
    scrollToIndexNow(val: number, type: number = 1, ...args: any[]) {
        if (val >= this.itemDataListMain.length) {
            val = this.itemDataListMain.length - 1
        }
        if (val < 0) { return }
        this.scrollView.stopAutoScroll()
        this.baseIndex = val

        let scrollPosIndex = this.baseIndex
        if (this.itemDataListMain.length - this.baseIndex < this.instantiateCount) {
            this.baseIndex -= this.instantiateCount - (this.itemDataListMain.length - this.baseIndex)
        } else {
            if (type == 1) {
                this.baseIndex -= Math.ceil(this.instantiateCount / 2)
            } else if (type == 3) {
                this.baseIndex -= (this.lieCount - 1) * this.hangCount
            }
        }
        if (this.scrollDir == ScrollDirEnum.GRID) {
            this.baseIndex -= this.baseIndex % this.hangCount
        }
        this.baseIndex = Math.max(this.baseIndex, 0)

        // 从第几个标签开始显示
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                let _x = -(this.setPosX(scrollPosIndex) - this.itemDistanceX / 2)
                // 判断是否超过边界
                if (_x < this.scrollMaxOffsetX) {
                    scrollPosIndex = this.itemDataListMain.length - this.instantiateCount
                    this.scrollView.content.x = this.scrollMaxOffsetX
                } else {
                    if (type == 1) {
                        this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2), 0)
                        if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2) >= 0) {
                            this.baseIndex = 0
                        }
                    } else if (type == 2) {
                        this.scrollView.content.x = -(this.setPosX(scrollPosIndex) - this.itemDistanceX / 2)
                    } else {
                        scrollPosIndex++
                        this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - this.itemWidth / 2 - this.spacingY), 0)
                        if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - this.itemWidth / 2 - this.spacingY) >= 0) {
                            this.baseIndex = 0
                        }
                    }
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                let _y = (Math.abs(this.setPosY(scrollPosIndex)) - this.itemDistanceY / 2)
                if (_y > this.scrollMaxOffsetY) {
                    scrollPosIndex = this.itemDataListMain.length - this.instantiateCount
                    this.scrollView.content.y = this.scrollMaxOffsetY
                } else {
                    if (type == 1) {
                        this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2, 0)
                        if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2 <= 0) {
                            this.baseIndex = 0
                        }
                    } else if (type == 2) {
                        this.scrollView.content.y = Math.abs(this.setPosY(scrollPosIndex)) - this.itemDistanceY / 2
                    } else {
                        scrollPosIndex = this.scrollDir == ScrollDirEnum.VERTICAL ? (scrollPosIndex + 1) : scrollPosIndex + this.hangCount
                        this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY, 0)
                        if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY <= 0) {
                            this.baseIndex = 0
                        }
                    }
                }
                break
        }
        if (args.length > 0) {
            this.extraParams = args
        }
        this.fixItemNodes()
        // 如果content有子项目,则重制目标点位置
        if (this.itemListMain.length > 0) {
            for (let index = 0; index < this.itemListMain.length; index++) {
                // let i = this.baseIndex + index
                let elemnet = this.itemListMain[index]
                elemnet.index = this.baseIndex + index
                this.setPosX(elemnet.index, elemnet.node)
                this.setPosY(elemnet.index, elemnet.node)
                this.setItemData(elemnet.node, this.itemDataListMain[elemnet.index], elemnet.index)
            }
        }
    }

    /**
     * 尝试滚动到滚动视图中心
     * @param index 标签
     * @param type  滚到哪里的类型(1.居中 2.顶部|左侧 3.底部｜右侧)
     * @param time 
     */
    scrollToIndex(index: number, type: number = 1, time: number = 1, offset: number = 0) {
        if (this.itemDataListMain[index] == null) {
            console.error("不存在此标签")
            return
        }
        switch (this.scrollDir) {
            case ScrollDirEnum.HORIZONTAL:
                if (type == 1) {
                    this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width / 2 + offset, this.scrollView.getScrollOffset().y), time)
                } else if (type == 2) {
                    this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.itemDistanceX / 2 + offset, this.scrollView.getScrollOffset().y), time)
                } else {
                    index++
                    this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width + offset, this.scrollView.getScrollOffset().y - this.itemWidth / 2 - this.spacingX), time)
                }
                break
            case ScrollDirEnum.VERTICAL:
            case ScrollDirEnum.GRID:
                if (type == 1) {
                    this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height / 2 + offset), time)
                } else if (type == 2) {
                    this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.itemDistanceY / 2 + offset), time)
                } else {
                    index = this.scrollDir == ScrollDirEnum.VERTICAL ? (index + 1) : index + this.hangCount
                    this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY + offset), time)
                }
                break
        }
        // this.scheduleOnce(() => {
        //     this.refreshItem(index)
        // }, time)
    }

    getItem(index: number): cc.Node | null {
        for (const key in this.itemListMain) {
            if (this.itemListMain[key].index == index) {
                return this.itemListMain[key].node
            }
        }
        return
    }

    // 使用对象池时，在切换界面时必须使用这个方法，将所有对象放到池中
    // @TODO PoolManager
    clear() {
        if (this.scrollView && this.scrollView.content) {
            this.scrollView.content.removeAllChildren()
        }
        // if (this.useNodePool) {
        //     this.itemListMain.forEach(element => {
        //         if (this.poolType == PoolEnum.ITEM_BAG) {
        //             PoolManager.putItemBag(element.node)
        //         } else if (this.poolType == PoolEnum.ITEM_BASE) {
        //             PoolManager.putItemBase(element.node)
        //         }
        //     });
        // } else {
        //     if (this.scrollView && this.scrollView.content) {
        //         this.scrollView.content.removeAllChildren()
        //     }
        // }
        this.itemDataList = []
        this.itemListMain = []
        this.itemListTitle = []
        this.itemDataListMain = []
        this.itemDataListTitle = []
        this.indexFrom = {}
    }
}