-- This --Source Code Form is subject to the terms of the bCDDL, v. 1.1.
-- If a copy of the bCDDL was not distributed with this
-- file, You can obtain one at http://beamng.com/bCDDL-1.1.txt

local M = {}
--Mandatory controller parameters
M.type = "auxiliary"
M.relevantDevice = nil

local abs = math.abs
local min = math.min
local max = math.max

local barrelNodes = {front = {}, back= {}}
  
local bulletBeams = {}
local bulletNodes = {}
local activeBullets = {}
local activeBulletCount = 0
local recoilCounter = 0
local muzzleFlashCounter = 0
local rofTimer = 0
local numberOfRounds = nil
local roundIndex = nil
local fireRateTime = nil
local bulletTargetSpeed = 0
local realBulletSpeed = 540
local realBulletSpeedSquared = realBulletSpeed * realBulletSpeed
local quarterBulletTargetSpeed = 0
local additionalExplosiveEnergy = 0 --J
local roundTimeout = 0.15

local function updateBulletFiring(bullet, dt)
  bullet.timeSinceFired = bullet.timeSinceFired + dt
  
  local nodeVelocity = obj:getNodeVelocityVector(bullet.nodeCid):length() - bullet.velocityOffset
  if nodeVelocity >= bulletTargetSpeed then
    electrics.values[bullet.thrusterSource] = nil
    recoilCounter = recoilCounter - 1
    local nodeMass = obj:getNodeMass(bullet.nodeCid)
    local targetEnergy = 0.5 * nodeMass * realBulletSpeedSquared + additionalExplosiveEnergy
    local equivalentWeight = min(max(targetEnergy * 2 / (nodeVelocity * nodeVelocity), nodeMass), 100)
    obj:setNodeMass(bullet.nodeCid, equivalentWeight)
    bullet.roundState = "fired"
    bullet.update = nop
  end
end

local function update(dt)
  for i = 1, activeBulletCount, 1 do
    activeBullets[i]:update(dt)
  end

  electrics.values.recoil = recoilCounter > 0 and 1 or nil --activates the recoil thruster
end

local function updateGFX(dt)
 --compatibility interface
 electrics.values.guns = electrics.values.gunFire or 0
 
 --------GUNS--------
  local activeBullets2 = {}
  for _,v in ipairs(activeBullets) do
    v.muzzleFlashTimer = max(v.muzzleFlashTimer - dt, 0)
    if v.muzzleFlashTimer <= 0 and v.hasMuzzleFlash then
      muzzleFlashCounter = muzzleFlashCounter - 1
      v.hasMuzzleFlash = false
    end
    if v.roundState == "fired" or v.timeSinceFired > roundTimeout then
      local bulletVelocity = abs(obj:getNodeVelocityVector(v.nodeCid):length())
      if bulletVelocity <= quarterBulletTargetSpeed then
        obj:breakBeam(v.beamCid or 0)
        obj:addParticleByNodes(v.nodeCid, 0.1, 0, 31, 0, 1)
        if v.hasMuzzleFlash then
          muzzleFlashCounter = muzzleFlashCounter - 1
          v.hasMuzzleFlash = false
        end
        v.roundState = "dead"
      end
    end
    if v.roundState ~= "dead" then
      table.insert(activeBullets2, v)
    end
  end

  activeBullets = activeBullets2
  --dump(activeBullets)

  rofTimer = max(rofTimer - dt, 0)
  if electrics.values.guns >= 1 and rofTimer <= 0 and roundIndex < numberOfRounds then --only go in here if we are STILL firing or want to fire
    --print(roundIndex)
    sounds.playSoundOnceAtNode("autocannon_12", barrelNodes.front[1], 5)	--Play the firing sound
    sounds.playSoundOnceAtNode("autocannon_12", barrelNodes.front[2], 5)

    obj:addParticleByNodesRelative(barrelNodes.front[1], barrelNodes.back[1], 10, 6, 0, 1)
    obj:addParticleByNodesRelative(barrelNodes.front[2], barrelNodes.back[2], 10, 6, 0, 1)

    gui.message(string.format("Cannon rounds: %d", (numberOfRounds - roundIndex - 2)), 3, "cannonrounds")
    for i = 1, 2, 1 do
      local thrusterSource = "shell_"..(roundIndex + i)
      electrics.values[thrusterSource] = 1 --roundIndex starts at 1, and determines which bullet is to be fired. it is added to "shell_", so if roundIndex is 1 then the thruster with "bllt_1" as the control is activated
      table.insert(activeBullets,
        {
          beamCid = bulletBeams[roundIndex + i],
          nodeCid = bulletNodes[roundIndex + i],
          thrusterSource = thrusterSource,
          velocityOffset = electrics.values.airspeed,
          muzzleFlashTimer = 0.05,
          hasMuzzleFlash = true,
          roundState = "firing",
          timeSinceFired = 0,
          update = updateBulletFiring})
    end
    rofTimer = fireRateTime
    roundIndex = roundIndex + 2
    recoilCounter = recoilCounter + 2
    muzzleFlashCounter = muzzleFlashCounter + 2
  end

  activeBulletCount = #activeBullets
  --print(activeBulletCount)
  --dump(activeBullets)
  electrics.values.muzf = muzzleFlashCounter > 0 and 1 or 0	--show the muzzleflash. This is done with glowmaps with "muzf" as their input.
end

local function init(jbeamData)
  recoilCounter = 0
  muzzleFlashCounter = 0
  roundIndex = 0
  
  electrics.values.guns = 0 --gun trigger
  electrics.values.recoil = nil --gun recoil
  electrics.values.muzf = 0 --muzzle flash
  numberOfRounds = jbeamData.numberOfRounds or 0
  fireRateTime = 0.115
  bulletTargetSpeed = jbeamData.bulletSpeed or 410
  quarterBulletTargetSpeed = bulletTargetSpeed * 0.25
  additionalExplosiveEnergy = jbeamData.bulletAdditionalExplosiveEnergy or 536000
  
  engineNodes = {L= {}, R = {}}
  barrelNodes = {front = {}, back= {}}
  
  for k, node in pairs (v.data.nodes) do
    if node.name == "gun1r" then barrelNodes.front[1] = k end
    if node.name == "gun1l" then barrelNodes.front[2] = k end
    if node.name == "gun0r" then barrelNodes.back[1] = k end
    if node.name == "gun0l" then barrelNodes.back[2] = k end

    if node.bullet then bulletNodes[node.bullet] = node.cid  end
  end
  
  activeBullets = {}
  activeBulletCount = 0
  bulletBeams = {}
  
  for _, beam in pairs(v.data.beams) do
    if beam.tracer then bulletBeams[beam.tracer] = beam.cid  end
  end
  
end

-- public interface
M.rri = function() roundIndex = 0 end
M.init = init
M.updateGFX = updateGFX
M.update = update
return M



