import { _decorator, Component, Prefab, Node, instantiate, Vec3, UITransform } from 'cc';
import { ViewItemPro } from './ViewItemPro';
import { SplitInfo } from '../ExClass';

const { ccclass, property,menu } = _decorator;

@ccclass('Generate')
@menu("新组件/Generate")
export class Generate extends Component {

    @property(Prefab)
    public pre_item: Prefab | Node = null;

    @property({ tooltip: "第一帧加载个数" })
    public first_add_num = 1;
    @property({ tooltip: "每帧加载个个数" })
    public every_frame_add = 1;
    @property({ tooltip: "每行最多个数" })
    public horizontal_num = 1;
    @property({ tooltip: "是否需要计算子节点位置" })
    public auto_align_child = true;
    @property({ tooltip: "1 高度=item行数*高度\n2:1的基础上宽度=列数*宽度" })
    public change_size_mode = 1;
    @property({ tooltip: "item是否上有脚本" })
    public item_have_script = true;
    @property({ tooltip: "从左往右排" })
    public left_to_right = true;
    @property
    public split_child = false;

    private uitrans = null;
    private m_data: any = null;
    private item_list: Array<any> = [];
    private state: number = 0;
    private have_init = false;
    private first_node: Node = null;
    private need_refresh = false;
    private p: any = null;
    private item_width: number = 0;
    private item_height: number = 0;
    private offset_x: number = 0;
    private offset_y: number = 0;

    private offsets: Array<any> = [];
    private split_infos: Array<SplitInfo> = [];
    private size_override: Array<number> = null;

    protected onLoad() {
        this.uitrans = this.node.getComponent(UITransform);
        this.first_node = null;
        if(!this.pre_item && this.node.children.length > 0){
            this.split_child = false;
            this.pre_item = this.node.children[0];
            if(this.item_have_script){
                this.item_list.push(this.pre_item.getComponent(ViewItemPro));
            }
            else{
                this.item_list.push(this.pre_item.components[0]);
            }
        }
    }
    protected onEnable(): void {
        if(this.need_refresh)this.initData(this.m_data, this.p);
    }
    public setOverrideSize(size:any){
        this.size_override = size;
        this.have_init = false;
    }

    public setPrefab(pre: Prefab) {
        this.pre_item = pre;
        this.first_node = instantiate(pre);
    }

    public initData(data: any, p: any = null) {
        this.m_data = data;
        this.p = p;
        this.need_refresh = !this.enabled;
        if(!this.enabled)return;
        this.initParams();
        this.updateSize();
        if (!data || data.length == 0) {
            this.item_list.forEach((element, index) => {
                element.node.active = false;
                if(this.split_child)this.split_infos[index].setActive(false);
            });
            return;
        }
        if (this.change_size_mode > 0 && this.auto_align_child) {
            this.updateItemsPos();
        }
        let items_len = this.item_list.length;
        let need_add = data.length - items_len;
        if (need_add > 0) {
            let need_add = data.length - items_len;
            need_add = Math.min(need_add, this.first_add_num);
            this.addItems(items_len, items_len + need_add);
            this.updateItems(0, items_len + need_add);
            this.state = 1;
            return;
        } 
        else if(need_add < 0){
            for (let i = items_len + need_add; i < items_len; i++){
                this.item_list[i].node.active = false;
                if(this.split_child)this.split_infos[i].setActive(false);
            }
        }
        this.updateItems(0, this.m_data.length);
    }

    private initParams() {
        if (!this.have_init) {
            let node = null;
            if(this.first_node){
                node = this.first_node;
            }
            else{
                if(Node.isNode(this.pre_item)){
                    node = this.node.children[0];
                }
                else{
                    node = instantiate(this.pre_item);
                }
                this.first_node = node;
                if (this.split_child) {
                    this.offsets = SplitInfo.getOffsetByNode(node);
                }
            }
            let trans = node.getComponent(UITransform);
            if(this.size_override){
                this.item_width = this.size_override[0];
                this.item_height = this.size_override[1];
            }
            else{
                this.item_width = trans.width;
                this.item_height = trans.height;
            }
            if(this.left_to_right)this.offset_x = trans.anchorX * this.item_width;
            else this.offset_x = (trans.anchorX - 1) * this.item_width;
            this.offset_y = (1 - trans.anchorY) * this.item_height;
            this.have_init = true;
        }
    }

    private updateSize() {
        let len = 0;
        if(this.m_data)len = this.m_data.length;
        let row = Math.ceil(len / this.horizontal_num);
        if (this.change_size_mode == 2) {
            let col = len > this.horizontal_num ? this.horizontal_num : len;
            this.uitrans.setContentSize(col * this.item_width, row * this.item_height);
        } 
        else if (this.change_size_mode == 1) {
            this.uitrans.height = row * this.item_height;
        }
    }

    private addItems(begin: number, end: number) {
        let begin_x = this.left_to_right ? this._getLeftBoundary() : this._getRightBoundary();
        let begin_y = this._getTopBoundary();
        let row = 0, col = 0;
        for (let i = begin; i < end; i++) {
            let node = i == 0 ? this.first_node : instantiate(this.pre_item) as Node;
            if(this.item_have_script)this.item_list.push(node.getComponent(ViewItemPro));
            else this.item_list.push(node.components[0]);
            node.setParent(this.node);
            let flag = this.left_to_right ? 1 : -1;
            let x = 0 , y = 0;
            if (this.auto_align_child) {
                row = Math.floor(i / this.horizontal_num);
                col = i % this.horizontal_num;
                x = begin_x + flag * col * this.item_width + this.offset_x;
                y = begin_y - row * this.item_height - this.offset_y;
                node.toXY(x, y);
            }
        }
        if (this.split_child) {
            let com_node = null;
            let info = null;
            for (let i = begin; i < end; i++) {
                com_node = this.item_list[i].node;
                info = new SplitInfo(com_node);
                this.split_infos.push(info);
                com_node.setSiblingIndex(i);
                info.setSlisByIndex(i);
                info.moveByOffset(this.offsets);
            }
            //console.log(this.node.children)
        }
    }

    private updateItemsPos() {
        if (this.item_list.length == 0) return;
        let begin_x = this.left_to_right ? this._getLeftBoundary() : this._getRightBoundary();
        let begin_y = this._getTopBoundary();
        let row = 0, col = 0;
        let x = 0 , y = 0;
        let flag = this.left_to_right ? 1 : -1;
        for (let i = 0, len = this.item_list.length; i < len; i++) {
            let node = this.item_list[i].node;
            row = Math.floor(i / this.horizontal_num);
            col = i % this.horizontal_num;
            x = begin_x + flag * col * this.item_width + this.offset_x;
            y = begin_y - row * this.item_height - this.offset_y;
            node.toXY(x, y);
            if(this.split_child){
                this.split_infos[i].moveByOffset(this.offsets);
            }
        }
    }
    private updateItems(begin: number, end: number) {
        for (let i = begin; i < end; i++) {
            this.item_list[i].node.active = true;
            if(this.split_child)this.split_infos[i].setActive(true);
            if(this.item_have_script){
                this.item_list[i].reset();
                this.item_list[i].init(this.m_data[i], i, this.p);
            }
            else{
                this.p(this.item_list[i].node, i);
            }
        }
    }

    private _getLeftBoundary() {
        return -this.uitrans.anchorX * this.uitrans.width;
    }
    private _getBottomBoundary() {
        return -this.uitrans.anchorY * this.uitrans.height;
    }
    private _getRightBoundary() {
        return (1 - this.uitrans.anchorX) * this.uitrans.width;
    }
    private _getTopBoundary() {
        return (1 - this.uitrans.anchorY) * this.uitrans.height;
    }

    public getValidCount(){
        return this.m_data.length;
    }
    public getItems() {
        return this.item_list;
    }

    public removeAllItems() {
        if(!this.isValid)return;
        for(let i = 0 ; i < this.item_list.length ; i++){
            this.node.removeChild(this.item_list[i].node);
        }
        this.have_init = false;
        this.item_list.length = 0;
        this.first_node = null;
    }

    public getItemById(index: number) {
        return this.item_list[index];
    }
    public getPosById(index:number){
        let begin_x = this.left_to_right ? this._getLeftBoundary() : this._getRightBoundary();
        let begin_y = this._getTopBoundary();
        let row = Math.floor(index / this.horizontal_num);
        let col = index % this.horizontal_num;
        let flag = this.left_to_right ? 1 : -1;
        let x = begin_x + flag * col * this.item_width + this.offset_x;
        let y = begin_y - row * this.item_height - this.offset_y;
        return [x, y];
    }
    protected update(dt: number) {
        if (this.state == 0) return;
        if (this.item_list.length >= this.m_data.length) {
            this.state = 0;
            //console.log(this.node.children)
            this.node.emit("load_end");
        } else {
            let add = Math.min(this.every_frame_add, this.m_data.length - this.item_list.length);
            let items_len = this.item_list.length;
            this.addItems(items_len, items_len + add);
            this.updateItems(items_len, items_len + add);
        }
    }

}
