const MINER = 0;
const FLYER = 1;
const ARCHER = 2;

function Entity( x , z , player , enemyPlayer )
{
    this.position = new BABYLON.Vector2( x , z );
    this.player = player;
    this.enemyPlayer = enemyPlayer;
    this.lasttime = Date.now();
    this.attacklasttime = Date.now();
    this.gateCounter = 0;
    this.target = new BABYLON.Vector2( 0 , 0 );
    this.targetEntity = null;
    this.targetBuilding = null;
    this.b_targetIsEntity = false;
    this.b_targetIsGate = false;
    this.b_targetIsBuilding = false;
    this.b_targetIsCore = false;
    this.b_isAtCore = false;

    this.modelFilename = "monkey.glb";

    this.type = MINER;
    this.b_flying = false;
    this.speed = 1+Math.random()/5;
    this.maxHP = 10;
    this.HP = this.maxHP;
    this.b_alive = true;
    this.deathCounter = 0;
    this.damage = 3;
    this.detectionRange = 1.5;
    this.attackRange = 0.05;

    this.coreDamage = 2;
    this.coreHitReward = 1;
    this.buildingDamage = 2;
}



Entity.prototype.update = function( dt )
{
    if ( !this.b_alive )
    {
        this.deathCounter++;
        return;
    }

    // when at the core, only attack the core, it's the goal of the game, so why should the mobs stop attacking the core when they are there
    if ( this.b_isAtCore ) 
    {
        if ( Date.now() > this.attacklasttime+1000 )
        {
            this.enemyPlayer.damageCore( this.coreDamage );
            this.player.giveGold( this.coreHitReward )
            this.attacklasttime = Date.now();
            sound_coreHit.play();
        }
        return;
    }

    if ( this.b_targetIsBuilding ) 
    {
        // if the building has died from another source
        if ( this.targetBuilding.building.isDead() )
        {
            this.b_targetIsBuilding = false;
            this.targetBuilding = null;
            this.findTarget();
        }
        else
        {
            var d = this.targetBuilding.building.worldPosition.subtract( this.position );
            var targetDistance = 0.25+this.attackRange;
            // are we at the target yet
            if ( d.length() < targetDistance )
            {
                // attack the enemy
                if ( Date.now() > this.attacklasttime+1000 )
                {
                    if ( this.targetBuilding.building.takeDamage( this.buildingDamage ) )
                    {
                        // if the building dies the takeDamage function returns true
                        this.player.giveGold( 5 ); // some gold for a building kill
                        this.b_targetIsBuilding = false;
                        this.targetBuilding = null;
                        this.findTarget();
                    }
                    this.attacklasttime = Date.now();
                }
            }
            else // move towards target
            {
                d.normalize();
                this.position = this.position.add( d.scale( this.speed*dt ) );
                if ( this.model != null )
                {
                    this.model.position.x = this.position.x;
                    this.model.position.z = this.position.y;
                }
            }
            return;
        }
    }

    if ( this.b_targetIsEntity )
    {
        // if the target entity has died from another source
        if ( !this.targetEntity.b_alive )
        {
            this.b_targetIsEntity = false;
            this.targetEntity = null;
            this.findTarget();
        }
        else
        {
            var d = this.targetEntity.position.subtract( this.position );
            var targetDistance = 0.1+this.attackRange;
            // are we at the target yet
            if ( d.length() < targetDistance )
            {
                // attack the enemy
                if ( Date.now() > this.attacklasttime+1000 )
                {
                    if ( this.targetEntity.takeDamage( this.damage ) )
                    {
                        // if the entity dies the takeDamage function returns true
                        this.player.giveGold( 1 ); // some gold for an entity kill
                        this.b_targetIsEntity = false;
                        this.targetEntity = null;
                        this.findTarget();
                    }
                    this.attacklasttime = Date.now();
                }
            }
            else // move towards target
            {
                d.normalize();
                this.position = this.position.add( d.scale( this.speed*dt ) );
                if ( this.model != null )
                {
                    this.model.position.x = this.position.x;
                    this.model.position.z = this.position.y;
                }

                // if the entities moves past the gates while targetting an entity, count the gate as passed anyway
                if ( this.player == player )
                {
                    if ( this.gateCounter == 0 )
                    {
                        if ( this.position.x > -3 ) this.gateCounter++;
                    }
                    else if ( this.gateCounter == 1 )
                    {
                        if ( this.position.x > 3 ) this.gateCounter++;
                    }
                }
                else
                {
                    if ( this.gateCounter == 0 )
                    {
                        if ( this.position.x < 3 ) this.gateCounter++;
                    }
                    else if ( this.gateCounter == 1 )
                    {
                        if ( this.position.x < -3 ) this.gateCounter++;
                    }
                }
            }
            return; // at the moment stick to an entity target once one is aquired
        }
    }

    // every second try to find a new target, this will make sure the entities prioritize enemy entities
    if ( Date.now() > this.lasttime+1000 )
    {
        this.findTarget();
        this.lasttime = Date.now();
    }

    var d = this.target.subtract( this.position );
    var targetDistance = 0.1;
    if ( this.b_targetIsCore ) targetDistance = 0.5+this.attackRange;
    // are we at the target yet
    if ( d.length() < targetDistance )
    {
        if ( this.b_targetIsGate )
        {
            this.gateCounter++;
            this.findTarget();
            return;
        }
        else if ( this.b_targetIsCore )
        {
            // attack core
            this.b_isAtCore = true;
            return;
        }
    }
    else // move towards target
    {
        d.normalize();
        this.position = this.position.add( d.scale( this.speed*dt ) );
        if ( this.model != null )
        {
            this.model.position.x = this.position.x;
            this.model.position.z = this.position.y;
        }
    }
}



Entity.prototype.findTarget = function()
{
    this.b_targetIsEntity = false;
    this.b_targetIsGate = false;
    this.b_targetIsBuilding = false;
    this.b_targetIsCore = false;

    // flying mobs can directly move to the core
    if ( this.b_flying ) this.gateCounter = 2;

    // look for enemy entities in range
    for ( var i = 0 ; i < entities.length ; ++i )
    {
        if ( entities[i].player.ID == this.enemyPlayer.ID )
        {
            if ( entities[i].b_alive )
            {
                // miners cannot attack flying mobs
                if ( this.type == MINER )
                {
                    if ( !entities[i].b_flying )
                    {
                        var distance = BABYLON.Vector2.Distance( entities[i].position , this.position );
                        if ( distance <= this.detectionRange*this.detectionRange )
                        {
                            this.targetEntity = entities[i];
                            this.b_targetIsEntity = true;
                            return;
                        }
                    }
                }
                else
                {
                    var distance = BABYLON.Vector2.Distance( entities[i].position , this.position );
                    if ( distance <= this.detectionRange*this.detectionRange )
                    {
                        this.targetEntity = entities[i];
                        this.b_targetIsEntity = true;
                        return;
                    }
                }
            }
        }
    }

    // move forward, e.g. to the next gate, then to the next gate, and then to the core
    if ( this.gateCounter == 0 )
    {
        if ( this.position.y < 0 )
        {
            this.target.x = this.player.gate1X;
            this.target.y = this.player.gate1Z-0.25+Math.random()*0.5;
        }
        else
        {
            this.target.x = this.player.gate2X;
            this.target.y = this.player.gate2Z-0.25+Math.random()*0.5;
        }
        this.b_targetIsGate = true;
    }
    else if ( this.gateCounter == 1 )
    {
        if ( this.position.y < 0 )
        {
            this.target.x = this.enemyPlayer.gate1X;
            this.target.y = this.enemyPlayer.gate1Z-0.25+Math.random()*0.5;
        }
        else
        {
            this.target.x = this.enemyPlayer.gate2X;
            this.target.y = this.enemyPlayer.gate2Z-0.25+Math.random()*0.5;
        }
        this.b_targetIsGate = true;
    }
    else 
    {
        // attack an enemy building or the core?
        if ( Math.random() <= 0.7 )
        {
            // look for enemy buildings in range
            for ( var i = 0 ; i < buildings.length ; ++i )
            {
                if ( buildings[i].building.player.ID == this.enemyPlayer.ID )
                {
                    if ( !buildings[i].building.isDead() )
                    {
                        var distance = BABYLON.Vector2.Distance( buildings[i].building.worldPosition , this.position );
                        if ( distance <= this.detectionRange*this.detectionRange )
                        {
                            this.targetBuilding = buildings[i];
                            this.b_targetIsBuilding = true;
                            return;
                        }
                    }
                }
            }

            // if no building was found in range, just take the core
            this.target.x = this.enemyPlayer.coreX;
            this.target.y = this.enemyPlayer.coreZ;
            this.b_targetIsCore = true;
        }
        else
        {
            this.target.x = this.enemyPlayer.coreX;
            this.target.y = this.enemyPlayer.coreZ;
            this.b_targetIsCore = true;
        }
    }
}



Entity.prototype.spawn = function()
{
	// this.box = BABYLON.MeshBuilder.CreateBox( "Box" , {} , scene );
	// this.box.scaling.x = 0.1;
	// this.box.scaling.y = 0.2;
	// this.box.scaling.z = 0.1;
    // this.box.position.x = this.position.x;
    // this.box.position.z = this.position.y;
    // var material = new BABYLON.StandardMaterial( "material" , scene );
    // if ( this.player == player )
    // {
    //     material.diffuseColor = new BABYLON.Color3( 0 , 0 , 0.7 );
    // }
    // else 
    // {
    //     material.diffuseColor = new BABYLON.Color3( 0.7 , 0 , 0.7 );
    // }
    // material.specularColor = new BABYLON.Color3( 0 , 0 , 0 );
    // this.box.material = material;

    var pthis = this;
    this.model = null;
    loadModel( this.modelFilename , "entity" , scene , function( model )
    {
        pthis.model = model;
        pthis.model.position.x = pthis.position.x;
        pthis.model.position.z = pthis.position.y;
        // ai models should face the other way
        if ( pthis.player.ID == 2 )
        {
            pthis.model.rotation.y = Math.PI;
            for ( var i = 0 ; i < pthis.model._children.length ; ++i )
            {
                pthis.model._children[i].addRotation( 0 , Math.PI , 0 );
            }
        }
        pthis.model.computeWorldMatrix();
        if ( pthis.b_flying ) pthis.model.position.y = 0.4;
        pthis.healthBar = new HealthBar( scene , pthis.model , 0.25 , 0.02 , -0.4 );
        if ( pthis.player == player )
        {
            // for ( var i = 0 ; i < pthis.model._children.length ; ++i )
            // {
            //     if ( pthis.model._children[i].material )
            //     {
            //         pthis.model.material.albedoColor = new BABYLON.Color3( 0.7 , 0 , 0.7 );
            //     }
            // }
        }
        else
        {
            for ( var i = 0 ; i < pthis.model._children.length ; ++i )
            {
                if ( pthis.model._children[i].material )
                {
                    pthis.model._children[i].material.albedoColor = new BABYLON.Color3( 0.7 , 0 , 0.7 );
                }
            }
        }
    } );

    this.findTarget();
}



Entity.prototype.takeDamage = function( damage )
{
    this.HP -= damage;
    this.healthBar.update( this.HP , this.maxHP );
    if ( this.HP <= 0 )
    {
        this.HP = 0;
        this.remove();
        this.b_alive = false;
        if ( this.type == MINER ) sound_minerDead.play();
        else if ( this.type == FLYER ) sound_wyvernDead.play();
        else if ( this.type == ARCHER ) sound_archerDead.play();
        return true;
    }
    return false;
}



Entity.prototype.setSpeed = function( speed )
{
    this.speed = speed + Math.random()*speed/5;
}



Entity.prototype.setHP = function( hp )
{
    this.maxHP = hp;
    this.HP = this.maxHP;
}



Entity.prototype.remove = function( damage )
{
    // remove the mesh
    this.model.dispose();
}