// The deck, as the room draws it.
//
// A card is drawn, not typed. The first attempt set the pips as text — "♠" in a
// span — and at the size a card actually is, a suit character renders as a speck:
// the shape is whatever font happened to answer, it doesn't scale, and it can't
// be positioned to the half-row a real pip layout needs.
//
// So each face is one SVG on a 100×140 field (the proportions of a real card),
// with the suits as vector shapes. Everything below is coordinates on that field,
// which is why the pips land where a printed deck puts them instead of where a
// flexbox felt like putting them.
//
// This started life inside blackjack.js. It's out here because solitaire deals
// off the same deck, and the second table in a casino is exactly the moment a
// copied card renderer becomes two card renderers that drift.
//
// Exposed as window.PeteCards. Nothing in here knows what game is being played.
(function () {
  "use strict";

  var SUIT_ART = {
    "♠": '<path d="M50 6C50 6 16 34 16 58a17 17 0 0 0 28 13c-1 12-5 19-12 25h36c-7-6-11-13-12-25a17 17 0 0 0 28-13C84 34 50 6 50 6Z"/>',
    "♥": '<path d="M50 96C50 96 8 66 8 38A22 22 0 0 1 50 24 22 22 0 0 1 92 38c0 28-42 58-42 58Z"/>',
    "♦": '<path d="M50 4 88 50 50 96 12 50Z"/>',
    "♣": '<g><circle cx="50" cy="28" r="19"/><circle cx="24" cy="62" r="19"/><circle cx="76" cy="62" r="19"/>' +
         '<path d="M44 60h12l7 36H37Z"/></g>',
  };

  // Pip layouts, the way a real deck lays them out — which is not "N suits in a
  // row". [x, y] on the 100×140 field. The seven canonical rows sit at y = 27,
  // 41, 56, 70, 84, 99, 113; sevens, eights and tens carry a pip *between* two of
  // them, which is the whole reason this is a table of coordinates and not a
  // grid. Anything below the middle is printed upside down, so it is.
  var R = [0, 27, 41.4, 55.7, 70, 84.3, 98.6, 113]; // 1-indexed, R[4] is the middle
  var L = 30, C = 50, Rr = 70; // the three columns

  var PIPS = {
    "A":  [[C, 70, 2.1]],
    "2":  [[C, R[1]], [C, R[7]]],
    "3":  [[C, R[1]], [C, R[4]], [C, R[7]]],
    "4":  [[L, R[1]], [Rr, R[1]], [L, R[7]], [Rr, R[7]]],
    "5":  [[L, R[1]], [Rr, R[1]], [C, R[4]], [L, R[7]], [Rr, R[7]]],
    "6":  [[L, R[1]], [Rr, R[1]], [L, R[4]], [Rr, R[4]], [L, R[7]], [Rr, R[7]]],
    "7":  [[L, R[1]], [Rr, R[1]], [C, 48.5], [L, R[4]], [Rr, R[4]], [L, R[7]], [Rr, R[7]]],
    "8":  [[L, R[1]], [Rr, R[1]], [C, 48.5], [L, R[4]], [Rr, R[4]], [C, 91.5], [L, R[7]], [Rr, R[7]]],
    "9":  [[L, R[1]], [Rr, R[1]], [L, R[3]], [Rr, R[3]], [C, R[4]], [L, R[5]], [Rr, R[5]], [L, R[7]], [Rr, R[7]]],
    "10": [[L, R[1]], [Rr, R[1]], [C, 48.5], [L, R[3]], [Rr, R[3]], [L, R[5]], [Rr, R[5]], [C, 91.5], [L, R[7]], [Rr, R[7]]],
  };

  var COURT = { "J": "Jack", "Q": "Queen", "K": "King" };
  var SUIT_NAMES = { "♠": "spades", "♥": "hearts", "♦": "diamonds", "♣": "clubs" };

  // One pip: the suit art, scaled and dropped at [x, y], turned over if it sits
  // below the middle of the card.
  function pipAt(suit, x, y, scale) {
    var s = (scale || 1) * 0.17;
    var turn = y > 70 ? " rotate(180 50 50)" : "";
    return '<g transform="translate(' + x + ' ' + y + ') scale(' + s + ') translate(-50 -50)' + turn + '">' +
      SUIT_ART[suit] + "</g>";
  }

  // The corner index: rank over suit. Printed in both corners, the second one
  // upside down, which is what lets you read a card from a fanned hand — and in
  // solitaire, from a column where all you can see is the top eighth of it.
  function index(face) {
    var g =
      '<g>' +
      '<text x="12" y="24" class="pete-card-idx">' + face.rank + "</text>" +
      '<g transform="translate(12 36) scale(0.13) translate(-50 -50)">' + SUIT_ART[face.suit] + "</g>" +
      "</g>";
    return g + '<g transform="rotate(180 50 70)">' + g + "</g>";
  }

  // paint draws the face. Every table uses this one, because they all deal out of
  // the same deck.
  function paint(front, face) {
    front.dataset.red = face.red ? "1" : "0";

    var body = "";
    if (COURT[face.rank]) {
      // Court cards: a framed panel, the suit above the letter and again below it
      // the other way up. A real court mirrors a *figure*; mirroring a letter just
      // stacks two of them into a blob, which is exactly what the first attempt
      // did. No portrait either — a drawn king would fight the room, and this
      // reads instantly at the size a card actually is.
      body =
        '<rect x="20" y="22" width="60" height="96" rx="6" class="pete-card-panel"/>' +
        pipAt(face.suit, 50, 38, 0.95) +
        '<text x="50" y="82" class="pete-card-court">' + face.rank + "</text>" +
        pipAt(face.suit, 50, 102, 0.95);
    } else {
      var spots = PIPS[face.rank] || [];
      for (var i = 0; i < spots.length; i++) {
        body += pipAt(face.suit, spots[i][0], spots[i][1], spots[i][2]);
      }
    }

    front.innerHTML =
      '<svg class="pete-card-svg" viewBox="0 0 100 140" xmlns="http://www.w3.org/2000/svg" ' +
      'role="img" aria-label="' + aria(face) + '">' + index(face) + body + "</svg>";
  }

  // "A♠" is not something a screen reader should be asked to pronounce.
  function aria(face) {
    var name = COURT[face.rank] || (face.rank === "A" ? "Ace" : face.rank);
    var suit = SUIT_NAMES[face.suit];
    return suit ? name + " of " + suit : face.label;
  }

  var dealt = 0; // how many cards this page has put down, ever — the tilt seed

  // el builds one card. face === null means face-down: the card is on the table,
  // but this browser has not been told what it is.
  //
  // opts.deal  — fly it in from the shoe (blackjack). Solitaire turns this off:
  //              it animates its own cards from wherever they actually came from,
  //              and a board that re-renders would otherwise re-deal itself out of
  //              the corner on every single move.
  // opts.tilt  — a degree or two of resting angle. A dealt hand wants it; a
  //              solitaire column does not, because thirteen tilted cards stacked
  //              an eighth of an inch apart read as a mistake rather than as a
  //              hand that was handled.
  function el(face, opts) {
    opts = opts || {};
    var deal = opts.deal !== false;
    var tilt = opts.tilt !== false;

    var wrap = document.createElement("div");
    wrap.className = "pete-card";
    wrap.dataset.face = face ? "up" : "down";
    if (face) wrap.dataset.key = face.label; // one deck, so the label is an id

    if (deal) {
      // Every card flies out of the shoe, which sits in the top-right of the felt.
      wrap.style.setProperty("--deal-x", "14rem");
      wrap.style.setProperty("--deal-y", "-6rem");
    } else {
      wrap.style.animation = "none";
    }
    wrap.style.setProperty("--tilt", tilt ? window.PeteFX.jitter(dealt++, 2.4).toFixed(2) + "deg" : "0deg");

    var inner = document.createElement("div");
    inner.className = "pete-card-inner";

    var front = document.createElement("div");
    front.className = "pete-card-front";
    var back = document.createElement("div");
    back.className = "pete-card-back";

    inner.appendChild(front);
    inner.appendChild(back);
    wrap.appendChild(inner);
    if (face) paint(front, face);
    return wrap;
  }

  // turnOver flips a face-down card up, now that we've been told what it is.
  //
  // Unlike el(), this is only ever a *gesture* — a card that was down and is now
  // up, because something happened. It never runs on a repaint, which is why the
  // flip sound can live here and be right for every table at once, and why the
  // deal sound cannot: el() runs every time a board is redrawn.
  function turnOver(wrap, face) {
    if (!wrap) return;
    paint(wrap.querySelector(".pete-card-front"), face);
    wrap.dataset.face = "up";
    wrap.dataset.key = face.label;
    if (window.PeteSFX) window.PeteSFX.play("flip");
  }

  window.PeteCards = {
    el: el,
    paint: paint,
    turnOver: turnOver,
    aria: aria,
    art: SUIT_ART,
  };
})();
