--# CardShuffle
-- The wash shuffle blackjack dealers do: every card spreads out face down
-- across the table, gets stirred around at random for a while, then gathers
-- back into one pile. Dealing the shuffled deck out to the shelves is a
-- later step, not done here.

Shuffle = class()

function Shuffle:init(cardTable)
  self.cardTable = cardTable
  self.state = nil -- nil, "spreading", "washing", or "gathering"
  self.timer = 0
  self.washElapsed = 0
  self.spreadDuration = 0.2
  self.washDuration = 1.5
  self.washInterval = 0.3 --how often cards get redirected to a new spot
  self.gatherDuration = 0.1
end

function Shuffle:isActive()
  return self.state ~= nil
end

function Shuffle:begin()
  if self:isActive() then return end
  self.state = "spreading"
  self.timer = 0
  self:clearShelves()
  for _, card in ipairs(self.cardTable.cards) do
    self.cardTable.arranger:cancel(card.body)
    card.showing = card.back
    self.cardTable.arranger:sendTo(card.body, self:randomWashPoint(), math.random() * 360)
  end
end

function Shuffle:clearShelves()
  for _, shelf in ipairs(self.cardTable.shelves.all) do
    for i = #shelf.cards, 1, -1 do
      shelf:release(shelf.cards[i])
    end
  end
  self.cardTable.shelves.shownShelf = nil
end

-- the patch of table the wash happens over, kept away from the shelf edges
function Shuffle:washArea()
  return vec2(WIDTH / 2, HEIGHT / 2), WIDTH * 0.4, HEIGHT * 0.4
end

function Shuffle:randomWashPoint()
  local center, halfW, halfH = self:washArea()
  local dx = (math.random() * 2 - 1) * halfW
  local dy = (math.random() * 2 - 1) * halfH
  return center + vec2(dx, dy)
end

function Shuffle:update()
  if self.state == "spreading" then
    self.timer = self.timer + DeltaTime
    if self.timer >= self.spreadDuration then self:startWashing() end
  elseif self.state == "washing" then
    self.timer = self.timer + DeltaTime
    self.washElapsed = self.washElapsed + DeltaTime
    if self.washElapsed >= self.washDuration then
      self:startGathering()
    elseif self.timer >= self.washInterval then
      self.timer = 0
      self:washTick()
    end
  elseif self.state == "gathering" then
    self.timer = self.timer + DeltaTime
    if self:allCardsSettled() or self.timer >= self.gatherDuration * 3 then
      self:finishGathering()
    end
  end
end

function Shuffle:startWashing()
  self.state = "washing"
  self.timer = 0
  self.washElapsed = 0
  self:washTick()
end

-- send every card off to a fresh random spot; getting redirected before it
-- arrives at the last one is what makes the pile look stirred rather than
-- just relocated
function Shuffle:washTick()
  for _, card in ipairs(self.cardTable.cards) do
    self.cardTable.arranger:sendTo(card.body, self:randomWashPoint(), math.random() * 360)
  end
end

-- cards can't share a point while they can hit each other, so hitting is
-- switched off for the gather: they overlap freely on the way in and square
-- up into one exact stack instead of several jittered near misses
function Shuffle:startGathering()
  self.state = "gathering"
  self.timer = 0
  local center = vec2(WIDTH / 2, HEIGHT / 2)
  for _, card in ipairs(self.cardTable.cards) do
    lab:canHitNothing(card.body)
    self.cardTable.arranger:sendTo(card.body, center, 0)
  end
end

function Shuffle:allCardsSettled()
  for _, card in ipairs(self.cardTable.cards) do
    if self.cardTable.arranger:isArranging(card.body) then return false end
  end
  return true
end

function Shuffle:finishGathering()
  self.state = nil
  for _, card in ipairs(self.cardTable.cards) do
    card.body.mask = {1}
  end
  self.cardTable.dealPrompt:show()
end
