import { system, TicksPerSecond,world,MolangVariableMap,EntityComponentTypes,EquipmentSlot} from '@minecraft/server'
import v3 from '../../common/v3.js'
import action from './abilities.js'

export default class mountLogic {
    #ns = "sqst_mnts"
    #cooldownStack = new Map();
    #activeToolStack = new Map();
    constructor (sqst_mounts) {
        this.sqst_mounts = sqst_mounts
        // console.log("mount logic")

        system.afterEvents.scriptEventReceive.subscribe(event => {
        if(event.id==this.#ns+":tick_mount")
            this.tickLoop(event.sourceEntity)
        
        if(event.id==this.#ns+":damage")
           console.log(event.sourceEntity.id+" PANIC!!")  
        if(event.id==this.#ns+":debug1")
            console.log(event.sourceEntity.id+" debug1")  
        if(event.id==this.#ns+":debug2")
            console.log(event.sourceEntity.id+" debug2")  
        })
/*
        world.afterEvents.entityHurt.subscribe(event => {
           console.warn(event.hurtEntity.typeId+" "+JSON.stringify(event.damageSource)+" d:"+event.damage+" h:"+event.hurtEntity.getComponent(EntityComponentTypes.Health).currentValue)  
        })
*/

        world.afterEvents.entitySpawn.subscribe(event => {
            if(event.entity.typeId.startsWith("sqst") && event.entity!==undefined){
                // Randomize the skin, based on weight
                if(event.entity.getProperty("sqst:prp_skin")!==undefined){
                    if(sqst_mounts[event.entity.typeId].skins!==undefined){
                        let chance = v3.getRandomInt(10)
                        let skin = -1;
                        let max = -1;
                        sqst_mounts[event.entity.typeId].skins.forEach((el,i) => {
                            if(el <= chance && el>max){
                                max = el
                                skin = i
                            }
                        });
                        //console.warn(chance+" "+skin)
                        this.setProp(event.entity, "sqst:prp_skin", skin)
                    }
                }
            }
        }) 
        world.beforeEvents.playerInteractWithEntity.subscribe(event => {
            //get rider
            let rider = event.target?.getComponent(EntityComponentTypes.Rideable)?.getRiders()[0]
            //if entity has rider and the rider is doing the interaction
            if(rider!==undefined && rider == event.player)
                {
                    //candel default action
                    event.cancel = true;
                    //exit if no interact ability
                    if(sqst_mounts[event.target.typeId].interact_ability===undefined) return;
                    //if it has an interact ability
                    if(sqst_mounts[event.target.typeId].interact_ability!==null){ 
                        //create cooldown tag from ability and id
                        let cooldownTag = sqst_mounts[event.target.typeId].interact_ability+event.target.id
                        //get active cooldown for entity if any 
                        let cooldown = this.#cooldownStack.get(cooldownTag)
                        //if no cooldown or cooldown expired
                        if(cooldown === undefined || cooldown+sqst_mounts[event.target.typeId].interact.delay+sqst_mounts[event.target.typeId].interact.cooldown<system.currentTick){
                            //do attack
                            system.run(()=>{action[sqst_mounts[event.target.typeId].interact_ability](
                                event.target,
                                rider,
                                sqst_mounts[event.target.typeId].interact
                            )})
                            //set new cooldown activation
                            this.#cooldownStack.set(
                                cooldownTag,
                                system.currentTick
                            )
                            
                        }
                        if(sqst_mounts[event.target.typeId].interact.show_cooldown){
                            //set active cooldown to be shown
                            this.#cooldownStack.set(
                                "active"+rider.id,
                                this.#cooldownStack.get(cooldownTag)
                            )
                            //re-get cooldown 
                            cooldown = this.#cooldownStack.get(cooldownTag)
                            //if cooldown activation exists show cooldown GUI
                            if(cooldown !== undefined && sqst_mounts[event.target.typeId].interact.show_cooldown)
                                system.run(()=>{this.cooldownUI(
                                    rider,
                                    sqst_mounts[event.target.typeId].interact.delay+sqst_mounts[event.target.typeId].interact.cooldown,
                                    cooldown,
                                    this.#ns+'.'+event.target.typeId.slice(this.#ns.length+1)+'.notif.interact',
                                    sqst_mounts[event.target.typeId].interact.duration
                                )})
                        }
                    }
                }
        })
        world.afterEvents.entityHitEntity.subscribe(event=>{
            //get rider
            let rider = event.hitEntity?.getComponent(EntityComponentTypes.Rideable)?.getRiders()[0]
            //if entity has rider and the rider is doing the damage
            if(rider!==undefined && rider == event.damagingEntity)
                {
                    //exit if no attack ability
                    if(sqst_mounts[event.hitEntity.typeId].attack_ability===undefined) return;
                    //if it has an attack ability
                    if(sqst_mounts[event.hitEntity.typeId].attack_ability!==null){ 
                        //create cooldown tag from ability and id
                        let cooldownTag = sqst_mounts[event.hitEntity.typeId].attack_ability+event.hitEntity.id
                        //get active cooldown for entity if any 
                        let cooldown = this.#cooldownStack.get(cooldownTag)
                        //if no cooldown or cooldown expired
                        if(cooldown === undefined || cooldown+sqst_mounts[event.hitEntity.typeId].attack.delay+sqst_mounts[event.hitEntity.typeId].attack.cooldown<system.currentTick){
                            //do attack
                            let start = action[sqst_mounts[event.hitEntity.typeId].attack_ability](
                                event.hitEntity,
                                rider,
                                sqst_mounts[event.hitEntity.typeId].attack
                            )
                            //set new cooldown activation
                            this.#cooldownStack.set(
                                cooldownTag,
                                start
                            )
                        }
                        if(sqst_mounts[event.hitEntity.typeId].attack.show_cooldown){
                            //set active cooldown to be shown
                            this.#cooldownStack.set(
                                "active"+rider.id,
                                this.#cooldownStack.get(cooldownTag)
                            )
                            //re-get cooldown 
                            cooldown = this.#cooldownStack.get(cooldownTag)
                            //if cooldown activation exists show cooldown GUI and is enabled
                            if(cooldown !== undefined)
                                this.cooldownUI(
                                    rider,
                                    sqst_mounts[event.hitEntity.typeId].attack.delay+sqst_mounts[event.hitEntity.typeId].attack.cooldown,
                                    cooldown,
                                    this.#ns+'.'+event.hitEntity.typeId.slice(10)+'.notif.attack',
                                    sqst_mounts[event.hitEntity.typeId].attack.duration
                                )
                        }
                    }

                }

        })
        world.afterEvents.itemUse.subscribe(event=>{
            if(event.itemStack.typeId=="sqst_mnts:locator"){

                let entId = event.source.getDynamicProperty("sqst:lastMount")
                if(entId!==undefined){
                    let entLoc = event.source.getDynamicProperty("sqst:lastMountPos").split("_")
                    let ent = {location:{
                        x:parseInt(entLoc[0]),
                        y:parseInt(entLoc[1]),
                        z:parseInt(entLoc[2])
                    }}
                    let locate = this.#activeToolStack.get(event.source.id)
                    if(locate === undefined) this.#activeToolStack.set(event.source.id,true)
                    else  this.#activeToolStack.set(event.source.id,!locate)
                    let movement = event.source.getComponent(EntityComponentTypes.Movement)
                    if(this.#activeToolStack.get(event.source.id)===true){
                        this.showDirection(event.source,ent,entId,movement);
                    }
                    
                }
            }
        })
        /*
        world.afterEvents.entityLoad.subscribe(event => {
            if(event.entity.typeId.startsWith("sqst")){
                let prp_speed = event.entity.getProperty("sqst:prp_speed")
                let prp_roll = event.entity.getProperty("sqst:prp_roll")
                if(prp_speed!== undefined && prp_speed>0){
                this.setProp(event.entity, "sqst:prp_speed", prp_speed = 0)
                let movement = event.entity.getComponent(EntityComponentTypes.Movement)
                    movement.setCurrentValue(movement.defaultValue)
                }
                if(prp_roll!== undefined && Math.abs(prp_roll)>1)
                    this.setProp(event.entity, "sqst:prp_roll", 1)
            }
        })*/
        /*
        world.beforeEvents.entityRemove.subscribe(event => {
            if(event.removedEntity.typeId.startsWith("sqst")){
                // name entity
                console.log(event.removedEntity.typeId)
            }
        }) */

        /*world.afterEvents.dataDrivenEntityTrigger.subscribe(event => {
            if(event.eventId === "sqst:ev_tame")
                event.entity.nameTag = sqst_mounts[event.entity.typeId].name
            //console.log(JSON.stringify(event.entity)+"|"+event.eventId+"||"+JSON.stringify(event.getModifiers()))
        })*/
       
    }

    showDirection(ent,dest,destEntId,movement){
        if(!this.#activeToolStack.get(ent.id)) return

        const equipmentCompPlayer = ent.getComponent(EntityComponentTypes.Equippable);
        if(equipmentCompPlayer?.getEquipment(EquipmentSlot.Mainhand)?.typeId!=="sqst_mnts:locator" || dest === undefined){
            this.#activeToolStack.set(ent.id,false)
            return
        } 

        const molang = new MolangVariableMap();
        molang.setColorRGB("color",{red:1,green:1,blue:1})

        if(v3.distance(ent.location,dest.location)<60){ //only test for chunk loading if closer then 60
            let loadTest =  ent.dimension.getBlock(v3.shiftUp(dest.location,-1))
            if(loadTest!==undefined){
                let mount = ent.dimension.getEntities({families:["sqstmnts"]}).find((e)=>e.id==destEntId);
                if(mount!==undefined){
                    dest = mount
                    ent.setDynamicProperty("sqst:lastMountPos",v3.dumps(mount.location,"_"))
                    molang.setColorRGB("color",{red:0,green:1,blue:0})
                }
            }
        }
        molang.setVector3("velocity",v3.multiply(ent.getVelocity(),4))
        molang.setVector3("target",v3.direction(ent.location,dest.location))
        molang.setSpeedAndDirection("actor",3,ent.getViewDirection())

        ent.dimension.spawnParticle("sqst_mnts:direction",v3.addV3(v3.shiftUp(ent.location,1.2),v3.multiply(ent.getVelocity(),2)),molang)
        system.runTimeout(()=>{this.showDirection(ent,dest,destEntId,movement)},5)
    }

    tickLoop(inputEnt){
        if(inputEnt===undefined)
            return

        let prp_speed = inputEnt.getProperty("sqst:prp_speed")
        let prp_roll = inputEnt.getProperty("sqst:prp_roll")

        // check if had rider
        let riding = inputEnt.getComponent(EntityComponentTypes.Rideable)
        let rider = riding?.getRiders()[0]

        if(riding !== undefined && rider !== undefined){
//--- IF SPEED ---
            if(rider.getDynamicProperty("sqst:lastMountPos")!=v3.dumps(inputEnt.location,"_")){
                rider.setDynamicProperty("sqst:lastMount",inputEnt.id)
                rider.setDynamicProperty("sqst:lastMountPos",v3.dumps(inputEnt.location,"_"))
            }

            if(prp_speed!==undefined){
                let speed = v3.magnitude(inputEnt.getVelocity())
                let movement = inputEnt.getComponent(EntityComponentTypes.Movement)
                //console.warn(movement.currentValue + " " +speed)
                //if moving in forward direction
                if(speed>0.1 && v3.angle(v3.xz(inputEnt.getVelocity()),v3.xz(inputEnt.getViewDirection()))<1 && !inputEnt.isInWater){
                    let nspeed = Math.pow(prp_speed*1.5,3);
                    if(nspeed>0.15){
                    const molang = new MolangVariableMap();
                    molang.setSpeedAndDirection("actor",movement.defaultValue+nspeed,inputEnt.getViewDirection())
                    rider.spawnParticle("sqst_mnts:speed",rider.getHeadLocation(),molang)
                    }
                    if(nspeed<0.3){
                        prp_speed+=0.01
                        this.setProp(inputEnt, "sqst:prp_speed", prp_speed)
                        movement.setCurrentValue(movement.defaultValue+nspeed)
                        inputEnt.setDynamicProperty("maxSpeed",false)
                    }else{
                        let particleLocation = inputEnt.location
                        try {
                            system.runTimeout(()=>{inputEnt.dimension.spawnParticle(this.#ns+":dust",particleLocation)},5)
                        } catch (error) {
                            console.warn("particle: "+error)
                        }
                        let woosh = inputEnt.getDynamicProperty("woosh")
                        if(woosh!== undefined && system.currentTick>woosh){
                            inputEnt.setDynamicProperty("woosh", system.currentTick + v3.getRandomInt(6)+5)
                            rider.playSound("sqst_mnts.pathrunner_woosh",{location:rider.location,pitch:Math.random()*0.5+0.7,volume:Math.random()*0.2+0.005})
                        }
                        if(inputEnt.getDynamicProperty("maxSpeed")!==undefined && !inputEnt.getDynamicProperty("maxSpeed")){
                            inputEnt.setDynamicProperty("maxSpeed",true)
                            inputEnt.setDynamicProperty("woosh", system.currentTick+v3.getRandomInt(6)+5)
                            rider.onScreenDisplay.setActionBar({ translate: this.#ns+'.notif.maxspeed', with: ["\n"] })
                        }
                    }

                     // set rider FOV change by applying speed 
                    if(prp_speed>0.2 && prp_speed<0.4)
                        rider.addEffect("speed",5,{amplifier:0.5, showParticles:false})
                    if(prp_speed>=0.4)
                        rider.addEffect("speed",5,{amplifier:1, showParticles:false})

                //if not moving but has speed or moving sideways or backwards
                }else if(prp_speed!=0) {
                    //reset speed
                    prp_speed=0
                    this.setProp(inputEnt, "sqst:prp_speed", prp_speed)
                    movement.setCurrentValue(movement.defaultValue)
                    inputEnt.setDynamicProperty("woosh", undefined)
                    rider.runCommand(`stopsound @s "sqst_mnts.pathrunner.woosh"`)
                }

            }
//--- END  SPEED ---
        //if no riders
        }else{
            //reset prps
            if(prp_speed!== undefined && prp_speed>0){
                this.setProp(inputEnt, "sqst:prp_speed", prp_speed = 0)
                let movement = inputEnt.getComponent(EntityComponentTypes.Movement)
                movement.setCurrentValue(movement.defaultValue)
                inputEnt.setDynamicProperty("woosh", undefined)
                //rider.runCommand(`stopsound @s "sqst_mnts.pathrunner.woosh"`)
            }
            if(prp_roll!== undefined && Math.abs(prp_roll)>1)
                this.setProp(inputEnt, "sqst:prp_roll", 1)
        }
    }

    setProp(inputEnt, prop, inputValue){
        try{
          inputEnt.setProperty(prop,inputValue)
          return true
        } catch (error) {
          console.warn(`setProp(${prop}): ${error}`)
          return false
        }
    }

    cooldownUI(inputEnt,duration,initial,text,usable=duration,activatedOn=system.currentTick){
        if(inputEnt === undefined || !inputEnt.getComponent(EntityComponentTypes.Riding)) return
        if(this.#cooldownStack.get("active"+inputEnt.id) !== initial) return

        let tick = system.currentTick
        let delta = tick-initial
        let green = duration-usable
       /* let delay = 2
        if(duration/10>delay){
            if(duration/10>40)
                delay = 40
            else 
                delay = duration/10
        }*/
        if(delta > duration){
            inputEnt.setDynamicProperty("AbilityUI",false)
            inputEnt.onScreenDisplay.setActionBar({ translate: this.#ns+'.notif.ready', with: ["\n"] })
            return
        }else{
            let p = Math.ceil((duration-delta)*34/duration)
            let d = Math.ceil(green*34/duration)
            let output = "§4";
            for(let i=1;i<=p;i++) output += "▮"
            output+="§r"
            for(let i=1;i<=34-p;i++) 
                {
                if(p+i<d)
                    output += "§7▮§r"
                else   
                    output += "§2▮§r"
                }
            output+="§r"
            if(tick-activatedOn > 59){ 
                    inputEnt.onScreenDisplay.setActionBar(" ")
                    return
            }else{
            let timeRemaining = ((duration-delta)/20).toFixed(1)
            inputEnt.onScreenDisplay.setActionBar({ translate: text, with: ["\n",output," cooldown: "+timeRemaining+"s"] })
            system.runTimeout(()=>{this.cooldownUI(inputEnt,duration,initial,text,usable,activatedOn)},2)
            }
        }
    }
}