'use strict';
// Extract the glyph outlines the site icons and the og-image wordmark need from a
// grotesque font (LiberationSans = Arial-metric, matching the game's system-ui/Arial
// stack) and emit cubic-bezier contours per character, baseline at y=0, y-down,
// plus the advance width so generate.js can lay out words without the font.
// Same outline conversion as ../../vk/build/extract.js, generalised to a char set.
const opentype = require('opentype.js');
const fs = require('fs');

const FONT = process.argv[2] || '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf';
const b = fs.readFileSync(FONT);
const font = opentype.parse(b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength));
const FS = 1000; // em scale

// The tile glyphs («Э», «8») + every character of the og-image wordmark lines
// («Эрудит», «Скрэббл — игра в слова»). The space carries only an advance.
const CHARS = [...new Set('Э8рудитСкэббл—игра в слова')];

function glyphData(ch) {
  const g = font.charToGlyph(ch);
  const p = g.getPath(0, 0, FS); // baseline at y=0, y-down
  const contours = [];
  let cur = null, prev = null;
  for (const c of p.commands) {
    if (c.type === 'M') {
      if (cur) contours.push(cur);
      cur = [{ v: [c.x, c.y], i: [c.x, c.y], o: [c.x, c.y] }];
      prev = { x: c.x, y: c.y };
    } else if (c.type === 'L') {
      cur.push({ v: [c.x, c.y], i: [c.x, c.y], o: [c.x, c.y] });
      prev = { x: c.x, y: c.y };
    } else if (c.type === 'C') {
      cur[cur.length - 1].o = [c.x1, c.y1];
      cur.push({ v: [c.x, c.y], i: [c.x2, c.y2], o: [c.x, c.y] });
      prev = { x: c.x, y: c.y };
    } else if (c.type === 'Q') {
      const c1 = [prev.x + 2 / 3 * (c.x1 - prev.x), prev.y + 2 / 3 * (c.y1 - prev.y)];
      const c2 = [c.x + 2 / 3 * (c.x1 - c.x), c.y + 2 / 3 * (c.y1 - c.y)];
      cur[cur.length - 1].o = c1;
      cur.push({ v: [c.x, c.y], i: c2, o: [c.x, c.y] });
      prev = { x: c.x, y: c.y };
    } else if (c.type === 'Z') {
      if (cur && cur.length > 1) {
        const last = cur[cur.length - 1], first = cur[0];
        if (Math.hypot(last.v[0] - first.v[0], last.v[1] - first.v[1]) < 1e-3) {
          first.i = last.i; // fold the duplicate closing point into the first
          cur.pop();
        }
      }
      if (cur) { contours.push(cur); cur = null; }
    }
  }
  if (cur) contours.push(cur);

  let minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinity;
  const out = contours.map(ct => {
    const v = [], i = [], o = [];
    ct.forEach(pt => {
      v.push(pt.v);
      i.push([pt.i[0] - pt.v[0], pt.i[1] - pt.v[1]]);
      o.push([pt.o[0] - pt.v[0], pt.o[1] - pt.v[1]]);
      minx = Math.min(minx, pt.v[0]); maxx = Math.max(maxx, pt.v[0]);
      miny = Math.min(miny, pt.v[1]); maxy = Math.max(maxy, pt.v[1]);
    });
    return { i, o, v, c: true };
  });
  const bbox = out.length
    ? { x: minx, y: miny, w: maxx - minx, h: maxy - miny }
    : { x: 0, y: 0, w: 0, h: 0 }; // the space has no outline
  return { adv: g.advanceWidth * (FS / font.unitsPerEm), bbox, contours: out };
}

const glyphs = {};
for (const ch of CHARS) glyphs[ch] = glyphData(ch);

const out = __dirname + '/glyphs.json';
fs.writeFileSync(out, JSON.stringify({ em: FS, glyphs }));
console.log('wrote', out, fs.statSync(out).size, 'bytes;', CHARS.length, 'glyphs:', CHARS.join(''));
