import { tipsType } from "./GameConst";

const BABYLON = window['BABYLON'];
export default class Game {
    config

    spFall = 2000 // 吃到道具掉落的秒数
    beyondFriends = [] // 超越好友配置
    targetScene
    meshMatUrl
    gameLevels
    addScoreConfig = {
        score:1,
        continuouslyScore:1
    }

    constructor(config,targetScene){
        let {spFall,beyondFriends,meshMatUrl,addScoreConfig,gameLevels} = config
        this.spFall = spFall
        this.beyondFriends = beyondFriends
        this.addScoreConfig = addScoreConfig
        this.targetScene = targetScene
        this.meshMatUrl = meshMatUrl
        this.gameLevels = gameLevels
        this.initScene()
    }

    public canvasWebgl: any;
    public scene: any;
    public camera: any;
    public light: any;
    public light2: any;
    public cloudPool: any[] = []; // 云的数组
    public cloudTemplate: any; // 云的模板
    public rocketP: any;
    public rocketLine: any;
    public rocket: any;
    public userMesh: any;
    public gravity: number = -0.98;
    public glowLayer: any; // 发光层
    public planet: any; // 星球
    public touchDown: boolean; // 是否点击屏幕
    public rotateSpeed: number = 0; // 旋转速度
    public lastPosition: any; // 最后的位置
    public startTimestamp: number; // 开始的时间戳
    public stopTimestamp: number; // 
    public score: number = 0; // 分数
    public downSpeed: number = 0.6; // 下落速度
    public downAddSpeed: number = 0.02; // 下落加速度
    public downMaxSpeed: number = 2;
    public createCount: number = 0; // 生成云的计数
    public cloudRange: number = 30; // 云生成的范围
    public TailSystem: any; // 尾巴粒子系统
    public eleTemplate: any; // 基本样本
    public cylinder: any; // 柱子
    public eleTemplatePool: any[] = [];// 物体样本池，用于存放克隆时使用的母体,用于克隆
    public nElePool: any[] = []; // 已经克隆的样本池
    public elementPool: any[] = []; // 所有基本样本克隆体的样本池,用于碰撞检测
    public spPool: any[] = []; // 道具池
    public mainObj: any; // 所有基本样本克隆体的parent;
    public lastY: number; // 最后一个圆环的y值
    public elementInterval: number = 20; // 圆环之间的间隔;
    public unitAngle = Math.PI / 6; // 单个元素所占原型的角度
    public redMaterial: any; // 红色材质
    public maxDepth: number = 0; // 最大深度
    private _layerNum: number = 0 // 层数
    private _score:number = 0 // 分数
    private isPause: boolean = false // 是否暂停
    private isCollision: boolean = true // 默认进行碰撞检测
    private degree:number = 1
    private offScreen
    private offScreenContext

    async initScene() {
        let container = document.getElementById("egretContainer");
        let w = document.documentElement.clientWidth || document.body.clientWidth;
        let h = document.documentElement.clientHeight || document.body.clientHeight;
        document.querySelectorAll("canvas")[0].style.position = "absolute";
        document.querySelectorAll("canvas")[0].setAttribute("touch-action", "none");
        if (!window['canvasWebgl']) {
            window['canvasWebgl'] = document.createElement("canvas");
            container.appendChild(window['canvasWebgl']);
        }
        window['canvasWebgl'].width = w;
        window['canvasWebgl'].height = h;

        let engine = new BABYLON.Engine(window['canvasWebgl'], true);
        engine.getRenderingCanvas = () => {
            return window['canvasWebgl']
        };

        
        // let canvas=document.createElement("canvas");
        // this.offScreen = canvas.transferControlToOffscreen()
        // this.offScreenContext = this.offScreen.getContext("2d")
        // let grad=this.offScreenContext.createLinearGradient(0,0,300,0); 
        // grad.addColorStop(0,"yellow");                  
        // grad.addColorStop(1,"green");
        // this.offScreenContext.fillStyle=grad;                         
        // this.offScreenContext.fillRect(0,0,1000,1000);
        // let skyBox = BABYLON.MeshBuilder.CreateBox("skyBox",{width:10000,height:10000,depth:10000,sideOrientation:2},this.scene)
        // let skyMat = new BABYLON.StandardMaterial("sky",this.scene)
        // // skyMat.diffuseTexture = new BABYLON.Texture( this.offScreen.toDataURL("image/png"), this.scene);
        // skyMat.diffuseColor = BABYLON.Color3.FromHexString("#36b2f8");
        // skyBox.material=skyMat
        this.scene = new BABYLON.Scene(engine);
        // this.scene.ambientColor = BABYLON.Color3.FromHexString("#ffffff");
        this.scene.clearColor = BABYLON.Color3.FromHexString("#e5efb0");
        this.scene.collisionsEnabled = true;

        this.light = new BABYLON.HemisphericLight("dir01", new BABYLON.Vector3(0, 1, 0.3), this.scene);
        this.light.intensity = 1;
        this.light.diffuse = new BABYLON.Color3(1, 1, 1);
        this.light.specular = new BABYLON.Color3(1, 1, 1);
        this.light2 = new BABYLON.HemisphericLight("dir02", new BABYLON.Vector3(0, -1, -0.3), this.scene);
        this.light2.intensity = 0.8;
        this.light2.diffuse = new BABYLON.Color3(1, 1, 1);
        this.light2.specular = new BABYLON.Color3(1, 1, 1);
        
        this.light.position = new BABYLON.Vector3(20, 60, 30);


        this.camera = new BABYLON.FreeCamera("Camera", new BABYLON.Vector3(0, 28, 45), this.scene);

        // this.glowLayer = new BABYLON.GlowLayer("glow", this.scene);
        // this.glowLayer.intensity = 2;
        
        // Fog
        // this.scene.fogMode = BABYLON.Scene.FOGMODE_EXP;
        // this.scene.fogDensity = 0.02;
        // this.scene.fogColor = BABYLON.Color3.FromHexString("#ffffff");

        this.redMaterial = new BABYLON.StandardMaterial("redMaterial", this.scene);
        this.redMaterial.diffuseColor = BABYLON.Color3.FromHexString("#e8781c");
        this.redMaterial.ambientColor = BABYLON.Color3.FromHexString("#e8781c");
        this.redMaterial.specularColor = BABYLON.Color3.FromHexString("#e8781c");
        this.redMaterial.backFaceCulling = false;

        // 样本元素
        await this.loadTemps()


        // 用户球体
        this.userMesh = BABYLON.Mesh.CreateSphere('user', 16, 2, this.scene);
        // this.userMesh.visibility = 0;
        this.rocket = BABYLON.Mesh.CreateBox('rocket', 2, this.scene);
        this.rocketP = BABYLON.Mesh.CreateBox('rocketP', 2, this.scene);
        this.rocket.parent = this.rocketP;
        this.rocket.visibility = 0;
        this.rocketP.visibility = 0;

        this.rocketP.ellipsoid = new BABYLON.Vector3(1, 1, 1);
        this.rocketP.ellipsoidOffset = new BABYLON.Vector3(0, 0, 0);
        let userMeshMaterial = new BABYLON.StandardMaterial("rocketMaterial", this.scene);
        userMeshMaterial.diffuseTexture = new BABYLON.Texture(this.meshMatUrl, this.scene);
        userMeshMaterial.ambientTexture = new BABYLON.Texture(this.meshMatUrl, this.scene);
        userMeshMaterial.backFaceCulling = false;
        this.userMesh.material = userMeshMaterial;
        this.userMesh.parent = this.rocket;

        this.rocketP.position.y = 10;
        this.rocketP.position.z = 13.5;
        this.lastPosition = this.rocketP.position.clone();
        this.userMesh.addRotation(0, 0, Math.PI);

        this.initTail();

        this.camera.parent = this.rocketP;
        this.camera.setTarget(this.rocketP.position)

        this.mainObj = BABYLON.Mesh.CreateBox('mainObj', 5, this.scene);
        this.mainObj.visibility = 0;

        this.cylinder = BABYLON.MeshBuilder.CreateCylinder("cy", { diameter: 13.5, height: 500 }, this.scene)
        let cylinderMaterial = new BABYLON.StandardMaterial("cylinderMaterial", this.scene);
        cylinderMaterial.diffuseColor = BABYLON.Color3.FromHexString("#bcfefa");
        cylinderMaterial.specularColor = BABYLON.Color3.FromHexString("#bcfefa");
        cylinderMaterial.ambientColor = BABYLON.Color3.FromHexString("#bcfefa");
        this.cylinder.material = cylinderMaterial

        let inputManager = this.camera.inputs;
        this.startTimestamp = Date.now();


        this.lastY = 0;
        this.maxDepth = 10;
        this.startTimestamp = Date.now();
        this.initElementPool()
        this.isPause = false
        this.isCollision = true
        this.scene.registerBeforeRender(this.onBeforeRender.bind(this))
    }



    loadTemps() {
        return new Promise<void>(r => {
            BABYLON.SceneLoader.ImportMesh("", "./resource/model/", `1.obj`, this.scene, (newMeshes) => {
                let element = newMeshes[0];
                let elementMaterial = new BABYLON.StandardMaterial("cloudtMaterial", this.scene);
                elementMaterial.diffuseColor = BABYLON.Color3.FromHexString("#189af0");
                elementMaterial.ambientColor = BABYLON.Color3.FromHexString("#189af0");
                elementMaterial.specularColor = BABYLON.Color3.FromHexString("#189af0");
                elementMaterial.backFaceCulling = false;
                element.material = elementMaterial;
                element.scaling.set(10, 10, 10);
                element.visibility = 0;
                this.eleTemplate = element;

                //e0
                let e0 = BABYLON.Mesh.CreateBox('e0P', 2, this.scene);
                e0.visibility = 0;
                e0['emptyRotationY'] = Math.PI / 2
                for (let i = 0; i < 2; i++) {
                    for (let t = 0; t < 3; t++) {
                        let e = this.eleTemplate.clone(`e0_${i}`);
                        e.visibility = 0;
                        e.rotation.y = t * Math.PI / 6 + i * Math.PI;
                        e.parent = e0;
                    }
                }
                this.eleTemplatePool.push(e0);
                // e1
                let e1 = BABYLON.Mesh.CreateBox('e1P', 2, this.scene);
                e1.visibility = 0;
                e1['emptyRotationY'] = Math.PI * 2
                for (let i = 0; i < 6; i++) {
                    let e = this.eleTemplate.clone(`e1_${i}`);
                    e.rotation.y = i * Math.PI / 3;
                    e.visibility = 0;
                    e.parent = e1;
                }
                this.eleTemplatePool.push(e1);
                //e2
                let e2 = BABYLON.Mesh.CreateBox('e2P', 2, this.scene);
                e2.visibility = 0;
                e2['emptyRotationY'] = Math.PI * 11 / 6
                for (let t = 0; t < 10; t++) {
                    let e = this.eleTemplate.clone(`e2_${t}`);
                    e.visibility = 0;
                    e.rotation.y = t * Math.PI / 6;
                    e.parent = e2;
                }
                this.eleTemplatePool.push(e2);
                //e3
                let e3 = BABYLON.Mesh.CreateBox('e3P', 2, this.scene);
                e3.visibility = 0;
                e3['emptyRotationY'] = Math.PI * 3 / 2
                for (let t = 0; t < 6; t++) {
                    let e = this.eleTemplate.clone(`e3_${t}`);
                    e.visibility = 0;
                    e.rotation.y = t * Math.PI / 6;
                    e.parent = e3;
                }
                this.eleTemplatePool.push(e3);
                //e4
                let e4 = BABYLON.Mesh.CreateBox('e4P', 2, this.scene);
                e4.visibility = 0;
                e4['emptyRotationY'] = Math.PI / 3
                for (let i = 0; i < 3; i++) {
                    for (let t = 0; t < 2; t++) {
                        let e = this.eleTemplate.clone(`e4_${i}`);
                        e.visibility = 0;
                        e.rotation.y = t * Math.PI / 6 + i * Math.PI * 2 / 3;
                        e.parent = e4;
                    }
                }
                this.eleTemplatePool.push(e4);

                r()
            })
        })

    }

    // 初始化圆环
    async initElementPool() {
        // 生成圆环池
        for (let i = 0; i < 10; i++) {
            if (this.eleTemplatePool.length > 0) {
                let num = Math.min(Math.floor(Math.random() * this.eleTemplatePool.length), this.eleTemplatePool.length - 1);
                let rY = this.eleTemplatePool[num].emptyRotationY ? this.eleTemplatePool[num].emptyRotationY : 0
                let nEle = this.eleTemplatePool[num].clone(`element_${num}`);
                let newAngle = Math.random() * Math.PI * 2;
                nEle.position.y = -(i + 1) * this.elementInterval;
                nEle.rotate(new BABYLON.Vector3(0, 1, 0), newAngle);
                nEle.parent = this.mainObj;
                this.lastY = nEle.position.y;
                this.nElePool.push(nEle);
                for (let e = 0; e < nEle['_children'].length; e++) {
                    let element = nEle['_children'][e];
                    element.visibility = 1;
                    this.elementPool.push(element);
                }
                
            }
        }
    }

    // 初始化尾巴
    initTail() {
        this.TailSystem = new BABYLON.ParticleSystem("particles", 1000, this.scene);
        this.TailSystem.particleTexture = new BABYLON.Texture("//yun.duiba.com.cn/db_games/activity/gravityBall1.0/model/fire.jpg", this.scene);
        this.TailSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;

        this.TailSystem.minAngularSpeed = -2;
        this.TailSystem.maxAngularSpeed = 2;
        this.TailSystem.minSize = 3;
        this.TailSystem.maxSize = 4;
        this.TailSystem.minLifeTime = 0.005;
        this.TailSystem.maxLifeTime = 0.015;
        this.TailSystem.minEmitPower = 0.5;
        this.TailSystem.maxEmitPower = 3.0;
        this.TailSystem.emitter = this.rocketP;
        this.TailSystem.emitRate = 10000;
        this.TailSystem.direction1 = new BABYLON.Vector3(-0.2, -0.2, -0.2);
        this.TailSystem.direction2 = new BABYLON.Vector3(0.2, 0.2, 0.2);
        this.TailSystem.color1 = new BABYLON.Color3(1, 0, 0);
        this.TailSystem.color2 = new BABYLON.Color3(0.95, 0.84, 0.05);
        this.TailSystem.stop()

        // this.TailSystem.moveSource = true;
        // this.TailSystem.start();
    }

    startTime = Date.now()
    touchSpeed = 0.1

    touchLeft(){
        this.touchSpeed = -0.1
    }
    touchRight(){
        this.touchSpeed = 0.1
    }

    continuouslyScoreFlag = 0
    scoreFlag = 0
    private onBeforeRender(){

        let now = Date.now()
        if (now - this.startTime > 1000) {
            this.startTime = now
        }
        if (now - this.startTime > 16.7) {
            this.startTime = now
            if (!this.isPause) { // 是否暂停
                this.createCount++;
                // 是否触控发生
                if(this.touchDown){
                    this.rotateSpeed = this.touchSpeed;
                }else{
                    this.rotateSpeed = 0;
                }
                this.mainObj.rotation.y -= this.rotateSpeed;
                this.accelerateByVector3(this.rocketP, new BABYLON.Vector3(0, -1, 0), this.downSpeed, this.downAddSpeed, false, -2)
                this.cylinder.position.y = this.maxDepth + 100
                // console.log(this.downSpeed)
                this.downSpeed += this.downAddSpeed
                if (this.downSpeed > this.downMaxSpeed) {
                    this.downSpeed = this.downMaxSpeed
                }
                if (this.eleTemplatePool.length > 0) {
                    if (this.rocketP.position.y < this.lastY + this.elementInterval * 5) {
                        let num = Math.min(Math.floor(Math.random() * 5), 4)
                        let nEle = this.eleTemplatePool[num].clone(`element_${num}`);
                        let rY = this.eleTemplatePool[num].emptyRotationY ? this.eleTemplatePool[num].emptyRotationY : 0
                        let newAngle = Math.random() * Math.PI * 2;
                        nEle.position.y = this.lastY - this.elementInterval;
                        nEle.rotate(new BABYLON.Vector3(0, 1, 0), newAngle);
                        nEle.parent = this.mainObj;
                        this.lastY -= this.elementInterval;
                        this.nElePool.push(nEle);
                        for (let e = 0; e < nEle['_children'].length; e++) {
                            let element = nEle['_children'][e];
                            let redEleNum = Math.min(Math.floor(Math.random() * this.eleTemplatePool.length), this.eleTemplatePool.length - 1);
                            if (e == redEleNum) {
                                element.gameType = 'redElement';
                                element.material = this.redMaterial.clone();
                            }
                            element.visibility = 1;
                            this.elementPool.push(element);
                        }
                        if (this.score % 2 == 0) {

                            let sp = BABYLON.MeshBuilder.CreateSphere("sp", { diameter: 2, segments: 8 }, this.scene);
                            this.spPool.push(sp)
                            sp.position.set(Math.cos(rY) * 13.5, nEle.position.y - 0.1, Math.sin(rY) * 13.5)
                            sp.parent = nEle
                        }
                    }

                    if (this.rocketP.position.y < this.maxDepth) {
                        if (Math.abs(this.rocketP.position.y - this.maxDepth) > this.elementInterval) {
                            this._layerNum += 1
                            let addScore = 1
                            if(this.continuouslyScoreFlag>0){
                                if(this.continuouslyScoreFlag>3 ){
                                    addScore = 2 * this._layerNum
                                }else{
                                    addScore = this.addScoreConfig.continuouslyScore
                                }
                            }else{
                                addScore =this.addScoreConfig.score
                            }
                            this.targetScene.addScore&&this.targetScene.addScore(addScore,this._layerNum);
                            this._score += addScore
                            if(this.degree<8){
                                for(let i=7;i>=1;i++){
                                    if(this._score > this.gameLevels['level'+i].endScore){
                                        // 改变难度
                                        this.handleTips(tipsType.levelChange)
                                        break
                                    }
                                }
                            }
                            if(this._score - this.scoreFlag > 200){
                                this.handleTips(tipsType.during200)
                                this.scoreFlag = this._score 
                            }
                            this.continuouslyScoreFlag++
                            if (this.beyondFriends.length > 0) {
                                for (let i = this.beyondFriends.length - 1; i >= 0; i--) {
                                    let f = this.beyondFriends[i]
                                    if (f.score < this._layerNum) {
                                        this.removeEle(f, this.beyondFriends)
                                        this.targetScene.beyondFriend(f.id)
                                        this.handleTips(tipsType.beyondFriend)
                                    }
                                }
                            }
                            this.nElePool.forEach(ele => {
                                if (ele.position.y > this.maxDepth - this.elementInterval - 1) {
                                    // console.log(ele)
                                    this.elementPool.forEach(element => {
                                        if (element.parent == ele) {
                                            let y = element.position.y + 50
                                            egret.Tween.get(element.position)
                                                .to({ x: 300 * Math.sin(element.rotation.y), y: y, z: 300 * Math.cos(element.rotation.y) }, 2000)
                                            egret.Tween.get(element.rotation)
                                                .to({ x: -6 * Math.PI * Math.random() }, 2000)
                                        }
                                    });
                                }
                            })
                            // 派发加分事件
                            this.maxDepth = this.rocketP.position.y;
                        }
                    }
                }
                if (this.isCollision) {
                    // 碰撞检测
                    for (let element of this.elementPool) {
                        if (element.intersectsMesh(this.userMesh, true)) {
                            this.continuouslyScoreFlag =0
                            if (element.gameType == 'redElement') {
                                // 撞到了黑云 游戏结束
                                this.isPause = true
                                this.targetScene.gameOver();
                                // 派发结束事件
                            } else {
                                // 如果碰到了就让速度反向
                                
                                this.downSpeed = -.7;
                                this.startTimestamp = Date.now()
                                // 
                            }
                        }
                    }


                    // 道具碰撞
                    for (let sp of this.spPool) {
                        if (sp.intersectsMesh(this.userMesh, true)) {
                            sp.visibility = 0
                            this.TailSystem.start()
                            this.downSpeed = this.downMaxSpeed
                            this.isCollision = false
                            setTimeout(() => {
                                this.TailSystem.stop()
                                this.isCollision = true
                            }, this.spFall)
                        }
                    }
                }


            }
        }
    }

    revive() {
        this.isPause = false
        this.isCollision = true
    }

    restart(config) {
        let {spFall,beyondFriends,meshMatUrl} = config?config:this.config
        this.spFall = spFall
        this.beyondFriends = beyondFriends
        this.meshMatUrl = meshMatUrl
        this.disposeAll()
        this.rocketP.position.y = 10;
        this.lastY = 0;
        this.maxDepth = 0;
        this.startTimestamp = Date.now();
        this.initElementPool()
        this.isPause = false
        this.isCollision = true
    }

    tiping=false
    handleTips(type){
        if(this.tiping){
            return
        }
        let idx = 5
        switch(type){
            case tipsType.beyondFriend:
                idx=1
                break
            case tipsType.levelChange:
                idx = 2    
                break
            case tipsType.during200:
                idx= 3    
                break
            case tipsType.pickRocket:
                idx =4    
                break
            case tipsType.bigScore:
                idx=5
                break
        }
        this.tiping = true
        this.targetScene.handleTips(idx)
        setTimeout(()=>{
            this.tiping = false
        },1000)
    }

    disposeAll() {
        for (let e = this.nElePool.length - 1; e >= 0; e--) {
            let element = this.nElePool[e];
            this.removeEle(element, this.nElePool);
            element.dispose();
        }
        for (let e = this.elementPool.length - 1; e >= 0; e--) {
            let element = this.elementPool[e];
            this.removeEle(element, this.elementPool);
            element.dispose();
        }
        this.nElePool = [];
        this.elementPool = [];
    }

    /** 
     * 让物体朝传入方向移动
     * 放在onEnterFrame里或者render里
     * 该方法里重力垂直向下
    */
    public moveByVector3(mesh: any, forward: any, speed?: number, isGravity?: boolean) {
        let _speed: number = 1;
        let _gravity = 0;
        let _isGravity = false;
        if (speed) {
            _speed = speed;
        }
        if (isGravity) {
            _isGravity = isGravity;
        }
        if (_isGravity && this.gravity) {
            _gravity = this.gravity;
        }
        mesh.position.x += forward.x * _speed;
        mesh.position.y += forward.y * _speed + _gravity;
        mesh.position.z += forward.z * _speed;
    }

    /**
     *  让物体朝传入方向加速/减速移动
     * */
    public tempSpeed: number = 0;
    public accelerateByVector3(mesh: any, forward: any, speed?: number, addSpeed?: number, isGravity?: boolean, maxSpeed?: number) {
        let _speed: number = 1;
        let _addSpeed: number = 0;
        let _gravity = 0;
        let _isGravity = false;

        if (speed) {
            _speed = speed;
        }
        if (addSpeed) {
            _addSpeed = addSpeed;
        }
        if (isGravity) {
            _isGravity = isGravity;
        }
        if (_isGravity && this.gravity) {
            _gravity = this.gravity
        }
        // v1 = v0 + a*t
        let newSpeed = _speed + _addSpeed;
        if (maxSpeed && newSpeed < maxSpeed) {
            newSpeed = maxSpeed
        }
        mesh.position.x += forward.x * newSpeed;
        mesh.position.y += forward.y * newSpeed + _gravity;
        mesh.position.z += forward.z * newSpeed;
    }

    /**
     *  移除数组里的元素
     *  @param e 要移除的元素
     *  @param arr 目标数组
     *  */
    public removeEle(e, arr) {
        var index = arr.indexOf(e);
        if (index >= 0) {
            arr.splice(index, 1)
        }
    }
}