--Character controller. Renders point particle.
local love = love
local marble = {}
local oldBuffer = love.graphics.newCanvas()
local newBuffer = love.graphics.newCanvas()
local oldState, curState, newState

local INERTIA= 0.03
local MAXSPEED = 3
local ddx, ddy = 0.0, 0.0

local function State( )
  return { t = 0, x = 0, y = 0, dx = 0, dy = 0 }
end

function marble.Current() return curState end

function marble.Next() return newState end

function marble.GetAcceleration( ) return ddx, ddy end

function marble.Integrate( step )
  newState.t = love.timer.getTime()
  newState.dx = (1.0 - INERTIA
) * curState.dx + INERTIA
 * ddx
  newState.dy = (1.0 - INERTIA
) * curState.dy + INERTIA
 * ddy
  newState.x = curState.x + newState.dx * step * MAXSPEED
  newState.y = curState.y + newState.dy * step * MAXSPEED
end

function marble.OnImpact( impact )
  --Adjust current trajectory according to collision.
  if not impact.dt then return end
  
  local x, y = impact.r * math.cos( impact.th ), impact.r * math.sin( impact.th )
  local vx, vy = newState.dx, newState.dy --Velocity of particle going into collision.
  local unx, uny = math.cos( impact.normal ), math.sin( impact.normal ) --Outward-facing normal of wave.
  local uvx, uvy --Unit vector velocity of particle.
  local speed = math.sqrt( vx * vx + vy * vy )
  if speed < 0.01 then
    --If the marble is motionless, there is no angular velocity wrt 0,
    --so the wave is headed directly inward.
    --We handle the collision as if the marble is headed directly outward.
    uvx, uvy = unx, uny
    vx, vy = uvx, uvy
  else
    uvx, uvy = vx / speed , vy / speed
  end
  
  
  
  --Get signed angle between normal and incoming velocity (both unit vectors)
  local dot = unx * uvy - uny * uvx
  
  --Fudge factor: apply an impulse inward so that you don't stick or slide on the wave.
  local inward = ( dot > 0 ) and dot or -dot
  inward = inward * inward * inward
  
  --Calculate the rotation matrix:
  --counterclockwise rotation by  2 * pi - 2 * arccos( n dot v )
  local c, s = 1 - 2 * dot * dot, - 2 * dot * math.sqrt( 1 - dot * dot )
  --Apply:
  local vxout, vyout =
  inward * (- math.cos(impact.th) ) - (1.0 - inward) * ( vx * c - vy * s ),
  inward * (- math.sin(impact.th) ) - (1.0 - inward) * ( x * s + vy * c )
  
  curState.x, curState.y = x, y
  curState.dx, curState.dy = vxout, vyout
  
  --[[debugRenderImpact = { xi = x, yi = y, xf = x - vx, yf = y - vy,
    xn = 0.2 * unx + x, yn = 0.2 * uny + y,
    vxout = x + vxout, vyout = y + vyout}]]
  
  return marble.Integrate( math.max( impact.dt , 1 / 120 ) ) --Hmm! Maybe this should be a fixed step instead for stability's sake.
end

function marble.Update()
  --Roll the log.
  for k, v in pairs( oldState ) do
    oldState[k] = curState[k]
    curState[k] = newState[k]
  end
end



function marble.OnKey()
  ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
  ddy = (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0)
  
  local n = math.sqrt( ddx * ddx + ddy * ddy )
  if n < 0.001 then return end
  ddx, ddy = ddx / n, ddy / n
end

function marble.OnVictory()
  
  --[[marble.Draw = function()
  
  --Extrapolate forward for slightly smoother rendering.
  local dt = love.timer.getTime() - curState.t 
  marble.Integrate( dt + 1 / 60.0 )
  
  local xp, yp = transform:transformPoint( oldState.x, oldState.y )
  local xc, yc = transform:transformPoint( curState.x, curState.y )
  local xn, yn = transform:transformPoint( newState.x, newState.y )
  
  love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255, 1.0 ) --Trans blue.  
  love.graphics.setLineJoin( "bevel" )
  love.graphics.setLineStyle( "smooth" )
  love.graphics.setLineWidth( 8.0 )
  love.graphics.line( xp, yp, xc, yc, xn, yn)
  love.graphics.circle( "fill", xn, yn, 4.0 )
  love.graphics.setCanvas()
  
    
  end]]
end

function marble.Draw()

  --Extrapolate forward for slightly smoother rendering.
  local dt = love.timer.getTime() - curState.t 
  marble.Integrate( dt + 1 / 60.0 )
  
  local xp, yp = transform:transformPoint( oldState.x, oldState.y )
  local xc, yc = transform:transformPoint( curState.x, curState.y )
  local xn, yn = transform:transformPoint( newState.x, newState.y )

  love.graphics.setCanvas( newBuffer )
  love.graphics.setColor(  1.0, 1.0, 1.0 , 0.99 ) -- White.
  love.graphics.draw( oldBuffer ) --Time-dependent fade: overlay canvas over itself.
  
  --Render latest segment in trail.
  love.graphics.setColor( 245 / 255, 169 / 255, 184 / 255, 1.0 ) --Trans pink.
  --love.graphics.line( xi, yi, xf, yf )
  --Segment stuff:
  love.graphics.setLineJoin( "bevel" )
  love.graphics.setLineStyle( "smooth" )
  love.graphics.setLineWidth( 8.0 )
  love.graphics.line( xp, yp, xc, yc, xn, yn)
  love.graphics.circle( "fill", xn, yn, 4.0 )
  
  love.graphics.setCanvas( oldBuffer )
  love.graphics.clear( 1.0, 1.0, 1.0, 0.0 )
  
  --Render circle directly to screen.
  love.graphics.setCanvas()
  love.graphics.draw( newBuffer )
  love.graphics.setColor( 1, 1, 1, 1.0 ) --White.
  love.graphics.setLineWidth( 1.0 )
  --love.graphics.circle( "line", xn, yn, 4 )
  
  oldBuffer, newBuffer = newBuffer, oldBuffer

end

marble._Draw = marble.Draw

function marble.Impact( impact )
  
end

--Window resize.
function marble.Resize()
  newBuffer = love.graphics.newCanvas()
  --TODO: render oldBuffer to new newBuffer, but scaled down.
  oldBuffer = love.graphics.newCanvas()
  

end

function marble.Reset()  
  oldState, curState, newState = State(), State(), State()
  marble.Resize()
  marble.Draw = marble._Draw
end

function marble.Canvas()
  return newBuffer
end



marble.Reset()

return marble