buttons = {}
buttons.a = false
buttons.b = false
buttons.c = false
buttons.d = false
buttons.e = false
buttons.f = false
buttons.moving = false
buttons.todo = false
buttons.axis = false
buttons.angle = 0
buttons.angleprogress = 0

cubies = {}
for x = 1, 8 do
  cubies[x] = ac.findNodes('Cube'..x):getTransformationRaw()
end

local sceneref = ac.findNodes('Car')
local cubemat = sceneref:getTransformationRaw()

local sharedData = ac.connect({
  ac.StructItem.key('himpman_says'..car.index),
  cube_goodness = ac.StructItem.double(),
  engine_solid = ac.StructItem.double(),
  steering_solid = ac.StructItem.double()
},false, ac.SharedNamespace.CarScript)

local booleanpeter, booleanpaul = false, false

Queue = {}
function Queue.new ()
  return {first = 0, last = -1}
end
function Queue.pushleft(q, value)
  local first = q.first - 1
  q.first = first
  q[first] = value
end
function Queue.pushright(q, value)
  local last = q.last + 1
  q.last = last
  q[last] = value
end
function Queue.popleft(q)
  local first = q.first
  if first > q.last then error("queue is empty") end
  local value = q[first]
  q[first] = nil -- to allow garbage collection
  q.first = first + 1
  return value
end
function Queue.length(q)
  return q.last - q.first + 1
end

local loopbuttons = { "a","b","c","d","e","f" }
local loopextras = { "extraA","extraB","extraC","extraD","extraE","extraF" }
local looprots = { "L", "U", "F", "R", "D", "B" }
local todoq = Queue.new()

crash = {}
crash.cooldown = 2
crash.depth = 0
crash.position = {0,0,0}
crash.v = 0
crash.pn = 0

function script.update(dt)
  --ac.debug("car extra", ac.getCar().extraA)
  for i = 1, #loopbuttons do
    booleanpeter, booleanpaul = pcall(getbutton,loopextras[i])
    if booleanpeter and booleanpaul then
      if buttons[loopbuttons[i]] == false then -- and buttons.moving == false
        buttons[loopbuttons[i]] = true
        --cubie_rotate(looprots[i])
        Queue.pushright(todoq,looprots[i])
      end
    else
      buttons[loopbuttons[i]] = false
    end
  end
  -- crash damage
  --ac.debug("collision depth", ac.getCar().collisionDepth)
  --ac.debug("collision pos", ac.getCar().collisionPosition)
  if crash.cooldown > 0 then crash.cooldown = crash.cooldown - dt end
  if ac.getCar().collisionDepth > 0.005 then
    if crash.cooldown <= 0 then
      crash.depth = ac.getCar().collisionDepth
      crash.position = ac.getCar().collisionPosition
      crash.pn = math.abs(crash.position.x)+math.abs(crash.position.y)+math.abs(crash.position.z)
      crash.cooldown = 0.25
      math.randomseed(crash.depth)
      for i = 1, math.min(math.floor(crash.depth*200),35) do
        crash.v = math.random()*crash.pn
        if crash.v < math.abs(crash.position.x) then
          if crash.position.x < 0 then
            Queue.pushright(todoq,"R")
          else
            Queue.pushright(todoq,"L")
          end
        elseif crash.v < math.abs(crash.position.x)+math.abs(crash.position.y) then
          if crash.position.y < 0 then
            Queue.pushright(todoq,"D")
          else
            Queue.pushright(todoq,"U")
          end
        else
          if crash.position.z < 0 then
            Queue.pushright(todoq,"F")
          else
            Queue.pushright(todoq,"B")
          end
        end
      end
    end
  end
  
  -- calculate whether the suspensions are in the right spot
  --ac.debug("Cube 4 diag", diagsum(cubies[4]))
  if diagsum(cubies[5]) + diagsum(cubies[6]) > 5.99 then
    -- engine is in place
    sharedData.engine_solid = 1
  else
    sharedData.engine_solid = 0
  end
  if diagsum(cubies[7]) + diagsum(cubies[8]) > 5.99 then
    -- steering is in place
    sharedData.steering_solid = 1
  else
    sharedData.steering_solid = 0
  end
  
  if buttons.moving then
    cubie_cont(dt)
  else
    if Queue.length(todoq) > 0 then
      cubie_rotate(Queue.popleft(todoq))
    end
  end
  -- check how the orientation is doing
  local orientation = 0
  for obj = 1, 8 do
    orientation = orientation + cubies[obj].row1.x*777 + cubies[obj].row2.y*365 + cubies[obj].row3.z*108.25
  end
  sharedData.cube_goodness = orientation
end

-- axis is xyz, a is angle.  it's fine
function better_rotate(inmatrix,axis,a)
  local rotmat = mat4x4.identity()
  if axis == "x" then
    rotmat = mat4x4(vec4(1, 0, 0, 0), vec4(0, math.cos(a), -math.sin(a), 0), vec4(0, math.sin(a), math.cos(a), 0), vec4(0, 0, 0, 1))
  end
  if axis == "y" then
    rotmat = mat4x4(vec4(math.cos(a), 0, math.sin(a), 0), vec4(0, 1, 0, 0), vec4(-math.sin(a),0,math.cos(a),0), vec4(0, 0, 0, 1))
  end
  if axis == "z" then
    rotmat = mat4x4(vec4(math.cos(a),-math.sin(a),0,0), vec4(math.sin(a),math.cos(a),0,0), vec4(0,0,1,0), vec4(0, 0, 0, 1))
  end
  inmatrix:set(inmatrix:mul(rotmat))
end

function getbutton(label)
  return car[label]
end

function diagsum(cubie)
  return(cubie.row1.x + cubie.row2.y + cubie.row3.z)
end

-- cubies are numbered, viewing from driver's pov
--  3 4             7 8
--  1 2  on top of  5 6
function cubie_rotate(face)
  local todo
  local axis
  local angle = math.pi
  local fa = face:sub(1,1)
  if fa == "R" then -- rotate R, ie. 2 4 8 6
    axis = "x"
    todo = {2,4,8,6}
    angle = -math.pi * 0.5
  elseif fa == "L" then -- rotate L, ie. 1 5 7 3
    todo = {1, 5, 7, 3}
    angle = math.pi * 0.5
    axis = "x"
  elseif fa == "F" then -- rotate F, ie. 1 2 6 5
    axis = "y"
    todo = {1, 2, 6, 5}
    angle = math.pi * 0.5
  elseif fa == "B" then -- rotate B, ie. 4 3 7 8
    axis = "y"
    todo = {4, 3, 7, 8}
    angle = -math.pi * 0.5
  elseif fa == "U" then -- rotate U, ie. 1 3 4 2
    axis = "z"
    todo = {1, 3, 4, 2}
    angle = math.pi * 0.5
  elseif fa == "D" then -- rotate D, ie. 5 6 8 7
    axis = "z"
    todo = {5, 6, 8, 7}
    angle = -math.pi * 0.5
  end
  local ce = face:sub(2,2)
  if ce == "'" then
    angle = -angle
    local rev = {}
    for i=#todo, 1, -1 do
      rev[#rev+1] = todo[i]
    end
    todo = rev
  end
  
  buttons.moving = true
  buttons.todo = todo
  buttons.axis = axis
  buttons.angle = angle
  buttons.angleprogress = 0
  local lastitem = cubies[todo[4]]
  cubies[todo[4]] = cubies[todo[3]]
  cubies[todo[3]] = cubies[todo[2]]
  cubies[todo[2]] = cubies[todo[1]]
  cubies[todo[1]] = lastitem
end

function cubie_cont(dt,instant)
  instant = instant or false
  local nextangle = 0
  if instant then
    nextangle = buttons.angle
    buttons.moving = false
  else
    -- 2.0 means 2 rotations per second
    nextangle = (2.0+Queue.length(todoq))*dt*buttons.angle
    if math.abs(nextangle + buttons.angleprogress) > math.abs(buttons.angle) then
      nextangle = buttons.angle - buttons.angleprogress
      buttons.moving = false
    end
    buttons.angleprogress = buttons.angleprogress + nextangle
  end
  for obj = 1, 4 do
    better_rotate(cubies[buttons.todo[obj]], buttons.axis, nextangle)
  end
end

local scramble = {'F','F',"R'","U'",'F','R','F','F','U','F','F','U'}
local possibilities = {'F',"F'",'B',"B'",'L',"L'",'R',"R'",'U',"U'",'D',"D'"}
math.randomseed(os.time())
--local scramble = {} -- for testing
for _, s in ipairs(scramble) do
  p = possibilities[math.random(1,#possibilities)]
  cubie_rotate(p)
  cubie_cont(0.03,true) -- run it immediately, no delay
end