local love = love
local step = 1.0 / 120
local sitelenpona
local text
local marble
local wave
local particles
local audio
local recorder
local DetectCollision
local OnImpact
local OnVictory


local state 
state = {

  Reset = function()
    state.beat = {}
    state.startTime = love.timer.getTime()
    state.currentBeat = 1
    state.timeToSimulate = 0.0
  end,

  timeToSimulate = 0.0,
  isGameStarted = false,
  beatScoreThreshold = 1.0,
  lastBeatScore = 0.0,
  currentBeat = 1,
  startTime = 0.0,

  beat = {
    t = nil,
    mu = nil,
  },
}

local function UpdateWindowTransform( w, h )
  local d = math.min( w, h )
  local r = love.graphics.get
  local tf = love.math.newTransform()

  local size = 3

  tf:translate( w / 2, h / 2)
  tf:scale( d / size, -d / size )
  transform = tf
end

function love.load()
  UpdateWindowTransform( love.graphics.getDimensions() )

  love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
  --love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.

  do--particle system setup
    particles = love.graphics.newParticleSystem(
      love.graphics.newImage( "prideflag.png" ),
      1024)

    particles:setSizes( 0.00015, 0.0001, 0.00018 )
    particles:setSizeVariation( 1 )
    particles:setRadialAcceleration( 0, 0.5 )
    particles:setSpeed( 0.2, 1 )
    particles:setLinearDamping( 1, 10 )
    particles:setRelativeRotation( true )
    particles:setEmitterLifetime( -1 )
    particles:setParticleLifetime( 0, 15 )
    particles:setSpread( 0.05 )
    particles:setColors(
      1, 1, 1, 1,
      91 / 255, 206 / 255, 250 / 255, 1,
      245 / 255, 169 / 255, 184 / 255, 1,
      1,1,1,0

    )
  end


  sitelenpona = assert( require "sitelenpona" )
  text = assert( require "text" )
  marble = assert( require "marble" )
  wave = assert( require "wave" )
  DetectCollision = assert ( require "collision" )
  audio = assert( require "audio" )
  recorder = assert( require "recorder" )
  return state.Reset()

end

local function ExtrapolateBeatScore( )
  local t = love.timer.getTime()
  local beat = state.beat
  if not beat.t then return 2.0 end
  if not beat.mu then return 2.0 end
  if beat.mu < 0.001 then return 2.0 end

  t = 1.1 * math.sin( math.pi * ( t - beat.t ) / beat.mu )
  return t * t * t * t
end

local function BeatScore( t )
  local beat = state.beat

  --Base case 1: first tap.
  if not beat.t then
    beat.t = t
    return 2.0
  end

  local dt = t - beat.t
  beat.t = t

  --Base case 2: second tap.
  if not beat.mu then
    beat.mu = dt
    return 2.0
  end

  local score = 1.1 * math.sin( math.pi * dt / beat.mu )
  score = score * score * score * score

  --General case: update average beat length.
  if dt > 0.2 then

    local WEIGHT = 0.85 --High number makes the last beat more significant.
    local mu = ( 1.0 - WEIGHT ) * beat.mu + WEIGHT * dt
    beat.mu = mu

  end

  return score
end

OnImpact = function( impact )
  if not impact then return end
  local score = BeatScore( impact.t )
  --DEBUG
  state.lastBeatScore = score
  local pass = false

  if score > 1.0 then

    pass = true
    state.currentBeat = state.currentBeat + 1
    if state.currentBeat >= 120 then
      return OnVictory()
    end
  end


  local x, y = math.cos(impact.th), math.sin(impact.th)
  particles:setPosition( impact.r * x, impact.r * y )
  particles:setEmissionArea( "normal", 0.1, 0.2, impact.th, true )
  particles:emit( 50 * score * score )

  audio.OnImpact( impact, score, pass, state.currentBeat )
  marble.OnImpact( impact )
  wave.OnImpact( impact )
end
local _OnImpact = OnImpact


local function NewGame()
  OnImpact = _OnImpact
  state.Reset()
  marble.Reset()
  wave.Reset()
end

 
OnVictory = function()
  OnImpact = function() end
end

function love.draw()


  --[[love.graphics.setColor(1.0, 1.0, 1.0)
  love.graphics.print( state.beat.mu or 0, 0)
  love.graphics.print( state.beat.t or 0, 0, 10)
  love.graphics.print( state.beatScoreThreshold, 0, 20)
  love.graphics.print( state.lastBeatScore, 0, 30 )]]

  local score = ExtrapolateBeatScore()

  love.graphics.push( "transform" )
  love.graphics.applyTransform( transform )



  wave.Draw( score )

  love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 )
  love.graphics.draw(particles, 0, 0)

  --[[if debugRenderImpact then

    love.graphics.setLineWidth( 0.01 )
    love.graphics.setColor( 1, 0, 0, 0.5 ) --Red: Incoming
    love.graphics.line(
      debugRenderImpact.xi,
      debugRenderImpact.yi,
      debugRenderImpact.xf,
      debugRenderImpact.yf)
    love.graphics.setColor( 0, 1, 0, 0.5 ) --Green: Normal
    love.graphics.line(
      debugRenderImpact.xi,
      debugRenderImpact.yi,
      debugRenderImpact.xn,
      debugRenderImpact.yn)
    love.graphics.setColor( 0, 0, 1, 0.5 ) -- Blue: Outgoing
    love.graphics.line(
      debugRenderImpact.xi,
      debugRenderImpact.yi,
      debugRenderImpact.vxout,
      debugRenderImpact.vyout)

  end]]

  love.graphics.pop()

  sitelenpona.Draw( text.tok[state.currentBeat] )

  marble.Draw()


end



function love.update( dt )

  audio.Update( ExtrapolateBeatScore() )
  particles:update( dt )
  dt = dt + state.timeToSimulate

  --Physics tick.
  while dt > step do
    recorder.Update( marble.GetAcceleration() ) --For savegames.

    marble.Integrate( step )
    wave.Integrate( step )

    OnImpact(
      DetectCollision( 
        marble.Current(), marble.Next(),
        wave.Current(), wave.Next() ))

    marble.Update()
    wave.Update()

    dt = dt - step
  end
  state.timeToSimulate = dt
end

function love.keypressed( key, code, isRepeat )
  if key == "escape" then return love.event.quit() end
  if key == "return" then return OnVictory() end
  if key == "space" then return NewGame() end
  return marble.OnKey()
end

function love.keyreleased( key, code )
  return marble.OnKey()
end

function love.resize( w, h )
  UpdateWindowTransform( w, h )
  if marble then marble.Resize() end
end