
-- Entity
Entity = { };

-- Metafunctions.
function Entity.__tostring( self )
    if( self.index ~= nil and _EntExists( self.index ) ) then
        return "Entity Index: " .. self.index;
    end
    
    return "Invalid Entity.";
end

-- Create a new entity from index.
function Entity:new( iobj )
    local obj = {};
    obj.index = nil;
    
    if( type( iobj ) == "number" ) then
        if( iobj > 0 ) then
            obj.index = iobj;
        end
    elseif( type( iobj ) == "string" ) then
        obj.index = _EntCreate( iboj );
    elseif( type( iobj ) == "table" ) then
        obj.index = iobj.index;
    end
    
    setmetatable( obj, self );
    self.__index = self;

    return obj;
end

-- Static methods.
---------------------------------------------------------------------

-- Get by name.
function Entity:GetByName( name )
    local index = _EntGetByName( name );
    if( index ~= 0 ) then
        return Entity:new( index );
    end
    return Entity:new( nil );
end

-- Find by class.
function Entity:FindByClass( class )
    local entities = {};
    local entids = _EntitiesFindByClass( class );
    for _, id in entids do
        table.insert( entities, Entity:new( id ) );
    end
    return entities;
end

-- Find by name.
function Entity:FindByName( name )
    local entities = {};
    local entids = _EntitiesFindByName( name );
    for _, id in entids do
        table.insert( entities, Entity:new( id ) );
    end
    return entities;
end

-- Find by name.
function Entity:FindInSphere( origin, radius )
    local entities = {};
    local org = origin;
    if( type(origin) ~= "userdata" ) then
        org = origin:GetVector3();
    end
    local entids = _EntitiesFindInSphere( org, radius );
    for _, id in entids do
        table.insert( entities, Entity:new( id ) );
    end
    return entities;
end

-- Normal methods.
---------------------------------------------------------------------

-- Check entity.
function Entity:Exists( )
    if( self.index ~= nil and _EntExists( self.index ) ) then
        return true;
    end
    return false;
end

-- Set keyvalue.
function Entity:KeyValue( key, value )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSetKeyValue( self.index, key, value );
end

-- Spawn.
function Entity:Spawn( )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSpawn( self.index );
end

-- Remove.
function Entity:Remove( )
    if( not self:Exists() ) then
        return;
    end
    
    _EntRemove( self.index );
end

-- Activate.
function Entity:Activate( )
    if( not self:Exists() ) then
        return;
    end
    
    _EntActivate( self.index );
end

-- Fire.
function Entity:Fire( action, value, delay )
    if( not self:Exists() ) then
        return;
    end
    
    _EntFire( self.index, action, value, delay );
end

-- Type.
function Entity:GetType( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetType( self.index );
end

-- Material.
function Entity:SetMaterial( material )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSetMaterial( self.index, material );
end

-- Set model.
function Entity:SetModel( model )
    if( not self:Exists() ) then
        return;
    end
    
    -- Precache it, in case they forgot.
    _EntPrecacheModel( model );
    _EntSetModel( self.index, model );
end

-- Get model.
function Entity:GetModel( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetModel( self.index );
end

-- Set name.
function Entity:SetName( name )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSetName( self.index, name );
end

-- Get name.
function Entity:GetName( name )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetName( self.index );
end

-- Get parent.
function Entity:GetParent( )
    if( not self:Exists() ) then
        return Entity:new(nil);
    end
    
    return Entity:new( _EntGetParent( self.index ) );
end

-- Set parent.
function Entity:SetParent( parent )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy support.
    if( type( parent ) == "table" ) then
        if( not parent:Exists() ) then
            return nil;
        end
        
        _EntSetParent( self.index, parent.index );
    else
        _EntSetParent( self.index, parent );
    end
end

-- Get max health.
function Entity:GetMaxHealth( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetMaxHealth( self.index );
end

-- Set max health.
function Entity:SetMaxHealth( max )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSetMaxHealth( self.index, max );
end

-- Get owner.
function Entity:GetOwner( )
    if( not self:Exists() ) then
        return Entity:new(nil);
    end
    
    return Entity:new( _EntGetOwner( self.index ) );
end

-- Set owner.
function Entity:SetOwner( owner )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy support.
    if( type( owner ) == "table" ) then
        if( not owner:Exists() ) then
            return;
        end
        
        _EntSetOwner( self.index, owner.index );
    else
        _EntSetOwner( self.index, owner );
    end
end

-- Set activity.
function Entity:SetActivity( seq )
    if( not self:Exists() ) then
        return;
    end
    
    _EntSetActivity( self.index );
end

-- Get water level.
function Entity:GetWaterLevel( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetWaterLevel( self.index );
end

-- Get position.
function Entity:GetPosition( )
    if( not self:Exists() ) then
        return Vector:new( 0, 0, 0 );
    end
    
    return Vector:new( _EntGetPos( self.index ) );
end

-- Set position.
function Entity:SetPosition( pos )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy.
    if( type( pos ) == "userdata" ) then
        _EntSetPos( self.index, pos );
    else
        _EntSetPos( self.index, pos:GetVector3() );
    end
end

-- Get angle.
function Entity:GetAngleNormalized( )
    if( not self:Exists() ) then
        return Vector:new( 0, 0, 0 );
    end
    
    return Vector:new( _EntGetAng( self.index ) );
end

-- Set angle.
function Entity:SetAngleNormalized( ang )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy.
    if( type( ang ) == "userdata" ) then
        _EntSetAng( self.index, ang );
    else
        _EntSetAng( self.index, ang:GetVector3() );
    end
end

-- Get angle.
function Entity:GetAngle( )
    if( not self:Exists() ) then
        return Vector:new( 0, 0, 0 );
    end
    
    return Vector:new( _EntGetAngAngle( self.index ) );
end

-- Set angle.
function Entity:SetAngle( ang )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy.
    if( type( ang ) == "userdata" ) then
        _EntSetAngAngle( self.index, ang );
    else
        _EntSetAngAngle( self.index, ang:GetVector3() );
    end
end

-- Get velocity.
function Entity:GetVelocity( )
    if( not self:Exists() ) then
        return Vector:new( 0, 0, 0 );
    end
    
    return Vector:new( _EntGetVelocity( self.index ) );
end

-- Set angle.
function Entity:SetVelocity( velocity )
    if( not self:Exists() ) then
        return;
    end
    
    -- Legacy.
    if( type( velocity ) == "userdata" ) then
        _EntSetVelocity( self.index, velocity );
    else
        _EntSetVelocity( self.index, velocity:GetVector3() );
    end
end

-- Get angle vectors.
function Entity:GetAngleVectors( )
    if( not self:Exists() ) then
        return Vector:new( 0, 0, 0 );
    end
    
    return Vector:new( _EntGetForwardVector( self.index ) ), Vector:new( _EntGetRightVector( self.index ) ), Vector:new( _EntGetUpVector( self.index ) );
end

-- Get phys attacker.
function Entity:GetPhysicsAttacker( )
    if( not self:Exists() ) then
        return Entity:new(nil);
    end
    
    return Entity:new( _EntGetPhysicsAttacker( self.index ) );
end

-- Set phys attacker.
function Entity:SetPhysicsAttacker( attacker )
    if( not self:Exists() ) then
        return nil;
    end
    
    -- legacy.
    if( type( attacker ) == "table" ) then
        if( not attacker:Exists() ) then
            return nil;
        end
        
        _EntSetPhysicsAttacker( self.index, attacker.index );
    else
        _EntSetPhysicsAttacker( self.index, attacker );
    end
end

-- Get ground entity.
function Entity:GetGroundEntity( )
    if( not self:Exists() ) then
        return Entity:new(nil);
    end
    
    return Entity:new( _EntGetGroundEntity( self.index ) );
end

-- Set ground entity.
function Entity:SetGroundEntity( entity )
    if( not self:Exists() ) then
        return nil;
    end
    
    -- legacy.
    if( type( entity ) == "table" ) then
        if( not entity:Exists() ) then
            return nil;
        end
        
        _EntSetGroundEntity( self.index, entity.index );
    else
        _EntSetGroundEntity( self.index, entity );
    end
end


-- Get movetype.
function Entity:GetMoveType( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetMoveType( self.index );
end

-- Set movetype.
function Entity:SetMoveType( movetype )
    if( not self:Exists() ) then
        return nil;
    end
    
    _EntSetMoveType( self.index, movetype );
end

-- Get movecollide
function Entity:GetMoveCollide( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetMoveCollide( self.index );
end

-- Set movecollide
function Entity:SetMaxHealth( val )
    if( not self:Exists() ) then
        return nil;
    end
    
    _EntSetMoveCollide( self.index, val );
end

-- Get Solid
function Entity:GetSolid( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetSolid( self.index );
end

-- Set solid
function Entity:SetSolid( val )
    if( not self:Exists() ) then
        return nil;
    end
    
    _EntSetSolid( self.index, val );
end

-- Get collision group
function Entity:GetCollisionGroup( )
    if( not self:Exists() ) then
        return nil;
    end
    
    return _EntGetCollisionGroup( self.index );
end

-- Set collisiongroup
function Entity:SetCollisionGroup( val )
    if( not self:Exists() ) then
        return nil;
    end
    
    _EntSetCollisionGroup( self.index, val );
end

-- IsPlayer.
function Entity:IsPlayer( )
    if( not self:Exists() ) then
        return false;
    end
    
    return IsPlayer( self.index );
end

-- Get physics entity.
function Entity:GetPhysicsEntity( )
    if( not self:Exists() ) then
        return nil;
    end
    
    -- If we have a physics object return it.
    if( _phys.HasPhysics( self.index ) ) then
        return PhysEntity:new( self.index );
    end
    
    return nil;
end
