--# CardTable

CardTable = class()

function CardTable:init()
  self.bounds = self:createScreenBorders()
  self.cards = {}
  self.cardsWithBodiesAsKeys = {}
  for _, suit in ipairs(Card.allSuits) do
    for rank = 2, 13 do self:addCard(Card(rank, suit)) end
    self:addCard(Card(1, suit)) --the ace goes on top
  end
  for _, card in ipairs(self.cards) do
    card.body.position = vec2(WIDTH / 2, HEIGHT / 2)
  end
  self.stacker = CardStacker(self.cards)
  self.arranger = CardArranger()
  self.shelves = Shelves(self)
  self.shuffle = Shuffle(self)
  self.deal = Deal(self)
  self.dealPrompt = DealPrompt(self)
end

function CardTable:beginShuffle()
  self.shuffle:begin()
end

function CardTable:dealShuffledDeck()
  self.deal:begin()
end

function CardTable:addCard(card)
  table.insert(self.cards, card)
  self.cardsWithBodiesAsKeys[card.body] = card
end

-- every card is the same size, so the shelves can ask once
function CardTable:cardWidth()
  return self.cards[1].width
end

function CardTable:cardHeight()
  return self.cards[1].height
end

function CardTable:createScreenBorders()
  local bounds = {
    lab.make:wall(vec2(0, 0), vec2(WIDTH, 0)),
    lab.make:wall(vec2(0, 0), vec2(0, HEIGHT)),
    lab.make:wall(vec2(WIDTH, 0), vec2(WIDTH, HEIGHT)),
    lab.make:wall(vec2(0, HEIGHT), vec2(WIDTH, HEIGHT))
  }
  for _, bound in ipairs(bounds) do bound.type = STATIC end
  return bounds
end

-- the clump this body belongs to, as a list that survives the next refresh.
-- shelved cards are left where they are.
function CardTable:stackContaining(body)
  local card = self.cardsWithBodiesAsKeys[body]
  if not card then return {} end
  local stack = self.stacker.stacks[card]
  if not stack then return {card} end
  local cards = {}
  for _, member in ipairs(stack) do
    if not member.body.shelf then table.insert(cards, member) end
  end
  return cards
end

-- every loose card the finger drew around, or drew across
function CardTable:cardsInLoop(points)
  local reach = self:cardWidth() * 0.5
  local caught = {}
  for _, card in ipairs(self.cards) do
    if not card.body.shelf then
      local at = card.body.position
      if pointInsideLoop(at, points) or nearLoopEdge(at, points, reach) then
        table.insert(caught, card)
      end
    end
  end
  return caught
end

function CardTable:draw()
  self.arranger:update()
  self.shelves:update()
  self.shuffle:update()
  self.deal:update()
  self.stacker:refreshStacks()
  tint(255, 136)
  sprite(asset.felt, WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)
  noTint()
  self.shelves:draw()
  for _, card in ipairs(self.cards) do card:draw() end
  for _, stack in ipairs(self.stacker.stacks) do
    if self:stackHasBadge(stack) then
      self:drawStackBadge(stack)
    end
  end
  self.dealPrompt:draw()
end

-- the badge hangs off the bottom card's upper right. its geometry lives here
-- so drawing it and tapping it can never drift apart.
function CardTable:badgeCenterFor(stack)
  local bottomCard = stack[#stack]
  return bottomCard.body.position + vec2(bottomCard.width * 0.5, bottomCard.height * 0.5)
end

function CardTable:badgeRadius()
  return self:cardWidth() * 0.175
end

function CardTable:stackHasBadge(stack)
  return #stack > 1 and not stack[1].body.shelf
end

-- the stack whose badge a touch landed on, if any
function CardTable:stackAtBadge(position)
  local radius = self:badgeRadius()
  for _, stack in ipairs(self.stacker.stacks) do
    if self:stackHasBadge(stack) then
      if self:badgeCenterFor(stack):dist(position) <= radius then return stack end
    end
  end
  return nil
end

function CardTable:drawStackBadge(stack)
  local cardW = stack[#stack].width
  local pos = self:badgeCenterFor(stack)
  pushStyle()
  fill(255, 14, 0, 245)
  ellipse(pos.x, pos.y, self:badgeRadius() * 2)
  fill(255)
  font("HelveticaNeue-Bold")
  fontSize(cardW * 0.2)
  text(#stack, pos.x, pos.y)
  popStyle()
end

-- keeping self.cards in the lab's order is what puts a picked up card in front
function CardTable:matchOrderTo(bodies)
  local reordered = {}
  for _, body in ipairs(bodies) do
    local card = self.cardsWithBodiesAsKeys[body]
    if card then table.insert(reordered, card) end
  end
  if #reordered == #self.cards then self.cards = reordered end
end

-- pulls a shelf's own cards out of the table's global draw/touch order and
-- reappends them in the shelf's own order, so overlap within that shelf
-- resolves the same way the shelf lays them out. topCard, if given, always
-- goes last regardless of its rank, which is used while it is being dragged.
function CardTable:syncShelfDrawOrder(shelf, topCard)
  local ordered = {}
  for _, card in ipairs(shelf.cards) do
    if card ~= topCard then table.insert(ordered, card) end
  end
  if topCard then table.insert(ordered, topCard) end
  local inShelf = {}
  for _, card in ipairs(shelf.cards) do inShelf[card] = true end
  
  local remainingCards = {}
  for _, card in ipairs(self.cards) do
    if not inShelf[card] then table.insert(remainingCards, card) end
  end
  for _, card in ipairs(ordered) do table.insert(remainingCards, card) end
  self.cards = remainingCards
  
  local inShelfBody = {}
  for _, card in ipairs(shelf.cards) do inShelfBody[card.body] = true end
  local remainingBodies = {}
  for _, body in ipairs(lab.bodies) do
    if not inShelfBody[body] then table.insert(remainingBodies, body) end
  end
  for _, card in ipairs(ordered) do table.insert(remainingBodies, card.body) end
  lab.bodies = remainingBodies
end
