@name bt_test_3
@inputs 
@outputs 
@persist Radius Balls:table Index Mass
@trigger none

#[
    Shoutout to Expression Advanced 2! Have you tried it yet?
    You should try it. It's a hundred times faster than E2
    and has more features. http://goo.gl/sZcyN9

    A new preprocessor command, @autoupdate has been added.
    See the wiki for more info.

    Documentation and examples are available at:
    http://wiki.wiremod.com/wiki/Expression_2
    The community is available at http://www.wiremod.com
]#

if (first()) {
    Radius = 4
    Mass = 15
    Index = 1
    
    function table table:rack( Pos:vector ) {
        holoCreate(Index, entity():toWorld(vec(-50, 0, 0)))
        holoScaleUnits(Index, vec(Radius))
        holoModel(Index, "hq_sphere")
        holoColor(Index, vec(255, 0, 0))
                
        This[Index, table] = table()
        This[Index, table]["pos", vector] = holoEntity(Index):pos()
        This[Index, table]["vel", vector] = vec()
                        
        for (N = 0, 4) {
            for (J = 0, N) {
                Index++
                
                holoCreate(Index, entity():toWorld(vec(50 + N*Radius, J*Radius - N*Radius/2, 0)))
                holoScaleUnits(Index, vec(Radius))
                holoModel(Index, "hq_sphere")
                
                This[Index, table] = table()
                This[Index, table]["pos", vector] = holoEntity(Index):pos()
                This[Index, table]["vel", vector] = vec()
            }
        }
        
        return This
    }

    function void table:collision( That:table ) {
        local Diff = That["pos", vector] - This["pos", vector]
        local Dist = Diff:length()
        
        if (Dist < Radius*2) {
            local Normal = Diff:normalized()
            local Delta = This["vel", vector] - That["vel", vector]
            local Dot = Delta:dot(Normal)
            
            if (Dot > 0) {
                local C = 0.1
                local I = (1 + C) * Dot #* (1 / Mass + 1 / Mass) 

                
                local Impulse = I*Normal
                This["vel", vector] = This["vel", vector] - Impulse #(Impulse / Mass)
                That["vel", vector] = That["vel", vector] + Impulse #(Impulse / Mass)
            }
        }
        
#[        vector difference = ball2.position - ball1.position
        float distance = sqrt(difference)
        if (distance < ball1.radius + ball2.radius) {
            vector normal = difference / distance
            vector velocityDelta = ball2.velocity - ball1.velocity
            float dot = dotProduct(velocityDelta, normal)
        
            if (dot > 0) {
                float coefficient = 0.5
                float impulseStrength = (1 + coefficient) * dot * (1 / ball1.mass + 1 / ball2.mass)
                vector impulse = impulseStrength * normal
                ball1.velocity -= impulse / ball1.mass
                ball2.velocity += impulse / ball2.mass
            }
        }]#


#[        local Dir = This["pos", vector] - B["pos", vector] 
        if (Dir:length() < Radius) {
            This["pos", vector] = This["pos", vector] - This["vel", vector]*0.06
            B["pos", vector] = B["pos", vector] - B["vel", vector]*0.06
            
            local Norm = Dir:normalized()
            local Temp = ((B["vel", vector] - This["vel", vector])*Norm)*Norm  * (Dir:length() / 2) #(Dir:normalized()/2)
                        This["vel", vector] = Temp
            B["vel", vector] = -Temp
            #This["vel", vector] = This["vel", vector] + Temp
            #B["vel", vector] = B["vel", vector] - Temp    
        }]#
    }   
    
    Balls = table():rack(vec()) 
    
    Balls[1, table]["vel", vector] = vec(100, 0, 0)
    
    #interval(500)
}

    
interval(60)

#while (opcounter() < maxquota()) {
for (I = 1, Index) {
    local ThisB = Balls[I, table]
    for (J = I + 1, Index) {
        ThisB:collision(Balls[J, table])    
    }
    
    ThisB["vel", vector] = ThisB["vel", vector]*0.975
    ThisB["pos", vector] = ThisB["pos", vector] + ThisB["vel", vector]*0.06
   # if (I == 1) { print(ThisB["vel", vector]) }

    holoPos(I, ThisB["pos", vector])
}

#}
#[
                    Ball.Dir = Ball.Dir * 0.975
                    Ball.Pos = Ball.Pos + Ball.Dir
                    Ball.Ent:setPos(Ball.Pos)
--@name bt_test_3
--@author 


local Holo, Timer, Ent, Sound = loadLibrary("holograms", "timer", "ents", "sounds")

local Balls = { }
local Self  = Ent:self()

local Diameter = 3

local function Collision(A, B)
    local Dir = B.Pos - A.Pos
    if Dir:Length() < Diameter then
        local Norm = Dir / Dir:Length()
        local Temp = ((A.Dir - B.Dir)*Norm)*Norm + (Dir:GetNormal() / 2)
        
        A.Dir = A.Dir - Temp
        B.Dir = B.Dir + Temp
        
        Sound.emitEntity(A.Ent, "physics/concrete/rock_impact_hard4.wav") 
        Sound.emitEntity(B.Ent, "physics/concrete/rock_impact_hard4.wav") 
    end
end

function Rack( Origin )
    local I, Idx, Rows, Radius = 0, 1, 5, Diameter

    Timer.create("Load", 1, 0, function () 
        if I < Rows then
            I = I + 1

            local J = I
            while J <= Rows do
                J = J + 1

                local Scale = Vector(Diameter, Diameter, Diameter) / 12
                local Pos   = Self:localToWorld(Origin + Vector(Radius*I, Radius*J - (Radius*(Rows+I + 2)) / 2, 6 + Radius / 2))
                local Holo  = Holo(Pos, Angle(0, 0, 0), "models/sprops/geometry/sphere_12.mdl", Scale)

                Idx = Idx + 1
                Balls[Idx] = {
                    Pos = Pos,
                    Dir = Vector(0, 0, 0),
                    Ent = Holo
                }
            end
        else
            Timer.remove("Load")
            
            local Count = Idx
            
            Timer.create("Run", 0.01, 0, function ()
                for i = 1, Count do
                    local Ball = Balls[i]
                    for j = i + 1, Count do
                        Collision( Balls[j], Ball )
                    end
                    
                    Ball.Dir = Ball.Dir * 0.975
                    Ball.Pos = Ball.Pos + Ball.Dir
                    Ball.Ent:setPos(Ball.Pos)
                end
            end)
 
        end
    end)
end

local BT = Holo(
    Self:getPos() + Vector(0,0,6),
    Self:getAngles(),
    "models/sprops/rectangles/size_54/rect_54x108x3.mdl",
    Vector(1,1,0.25)
)
BT:setColor(Color(10, 108, 3))


local TempPos = Self:localToWorld(Vector(36, 0, 6 + Diameter / 2))
Balls[1] = {
    Pos = TempPos,
    Dir = Vector(-5, 0, 0),
    Ent = Holo(TempPos, Angle(0, 0, 0), "models/sprops/geometry/sphere_12.mdl", Vector(Diameter, Diameter, Diameter) / 12)   
}

Rack( Vector(-36, 0, 0) )]#
