-- 3d vectors
-------------------------------------------
Vector = { x = 0, y = 0, z = 0 };

-- Meta methods.
function Vector.__add( a, b )
    local obj = Vector:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x + b, a.y + b, a.z + b );
    else
        obj:Set( a.x + b.x, a.y + b.y, a.z + b.z );
    end
    
    return obj;
end

function Vector.__sub( a, b )
    local obj = Vector:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x - b, a.y - b, a.z - b );
    else
        obj:Set( a.x - b.x, a.y - b.y, a.z - b.z );
    end
    
    return obj;
end

function Vector.__mul( a, b )
    local obj = Vector:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x * b, a.y * b, a.z * b );
    else
        obj:Set( a.x * b.x, a.y * b.y, a.z * b.z );
    end
    
    return obj;
end

function Vector.__div( a, b )
    local obj = Vector:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x / b, a.y / b, a.z / b );
    else
        obj:Set( a.x / b.x, a.y / b.y, a.z / b.z );
    end
    
    return obj;
end

function Vector.__tostring( a )
    return tostring( a.x ) .. " " .. tostring( a.y ) .. " " .. tostring( a.z );
end

-- Create.
function Vector:new( x, y, z )
    local obj = {};
    
    setmetatable( obj, self );
    self.__index = self;
    
    if( type(x) == "userdata" ) then
        obj:Set( x.x, x.y, x.z );
    else
        if( x and y and z ) then
            obj:Set( x, y, z );
        end
    end
    
    return obj;
end

-- Grab vector3.
function Vector:GetVector3()
    return vector3( self.x, self.y, self.z );
end

-- Set.
function Vector:Set( x, y, z )
    self.x = x;
    self.y = y;
    self.z = z;
end

-- Dot product.
function Vector:DotProduct( vec )
    return self.x * vec.x + self.y * vec.y + self.z * vec.z;
end

-- Cross product.
function Vector:CrossProduct( vec )
    local obj = Vector:new();
    obj:Set( self.y * vec.z - self.z * vec.y, self.z * vec.x - self.x * vec.z, self.x * vec.y - self.y * vec.x );
    return obj;
end

-- Length.
function Vector:Length( )
    return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z );
end

-- Normalize.
function Vector:Normalize()
    local length = self:Length();
    
    if( length ) then
        local iLength = 1 / length;
        self:Set( self.x * iLength, self.y * iLength, self.z * iLength );
    else
        self:Set( 0, 0, 0 );
    end
    
    return self;
end

-- Make 2d.
function Vector:Make2D( )
    local obj = Vector2D:new( );
    obj:Set( self.x, self.y );
    return obj;
end

-- 2d vectors.
-------------------------------------------
Vector2D = { x = 0, y = 0 };

-- Meta methods.
function Vector2D.__add( a, b )
    local obj = Vector2D:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x + b, a.y + b );
    else
        obj:Set( a.x + b.x, a.y + b.y );
    end
    
    return obj;
end

function Vector2D.__sub( a, b )
    local obj = Vector2D:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x - b, a.y - b );
    else
        obj:Set( a.x - b.x, a.y - b.y );
    end
    
    return obj;
end

function Vector2D.__mul( a, b )
    local obj = Vector2D:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x * b, a.y * b );
    else
        obj:Set( a.x * b.x, a.y * b.y );
    end
    
    return obj;
end

function Vector2D.__div( a, b )
    local obj = Vector2D:new( );
    
    if( tonumber( b ) ) then
        obj:Set( a.x / b, a.y / b );
    else
        obj:Set( a.x / b.x, a.y / b.y );
    end
    
    return obj;
end

function Vector2D.__tostring( a )
    return tostring( a.x ) .. " " .. tostring( a.y );
end

-- Create.
function Vector2D:new( vec3 )
    local obj = {};
    
    setmetatable( obj, self );
    self.__index = self;
    
    if( vec3 ~= nil ) then
        obj:Set( vec3.x, vec3.y );
    end
    
    return obj;
end

-- Set.
function Vector2D:Set( x, y )
    self.x = x;
    self.y = y;
end

-- Dot product.
function Vector2D:DotProduct( vec )
    return self.x * vec.x + self.y * vec.y;
end

-- Cross product.
function Vector2D:CrossProduct( vec )
    local obj = Vector2D:new();
    obj:Set( self.y * vec.z - self.z * vec.y, self.z * vec.x - self.x * vec.z );
    return obj;
end

-- Length.
function Vector2D:Length( )
    return math.sqrt( self.x * self.x + self.y * self.y );
end

-- Normalize.
function Vector2D:Normalize()
    local length = self:Length();
    
    if( length ) then
        local iLength = 1 / length;
        self:Set( self.x * iLength, self.y * iLength );
    else
        self:Set( 0, 0 );
    end
    
    return self;
end
