/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2022 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#ifndef __COSMOPOLITAN__
#include <stdio.h>
#include <wchar.h>
#endif
#include "blc.h"

static inline int bsr(unsigned long long x) {
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
  return __builtin_clzll(x) ^ 63;
#else
  static const char kDebruijn[64] = {
      0,  47, 1,  56, 48, 27, 2,  60, 57, 49, 41, 37, 28, 16, 3,  61,
      54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4,  62,
      46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
      25, 39, 14, 33, 19, 30, 9,  24, 13, 18, 8,  12, 7,  6,  5,  63,
  };
  x |= x >> 1, x |= x >> 2;
  x |= x >> 4, x |= x >> 8;
  x |= x >> 16, x |= x >> 32;
  return kDebruijn[(x * 0x03f79d71b4cb0a89) >> 58];
#endif
}

static unsigned long long tpenc(unsigned c) {
  static const unsigned short kTpEnc[32 - 7] = {
      1 | 0300 << 8, 1 | 0300 << 8, 1 | 0300 << 8, 1 | 0300 << 8, 2 | 0340 << 8,
      2 | 0340 << 8, 2 | 0340 << 8, 2 | 0340 << 8, 2 | 0340 << 8, 3 | 0360 << 8,
      3 | 0360 << 8, 3 | 0360 << 8, 3 | 0360 << 8, 3 | 0360 << 8, 4 | 0370 << 8,
      4 | 0370 << 8, 4 | 0370 << 8, 4 | 0370 << 8, 4 | 0370 << 8, 5 | 0374 << 8,
      5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8,
  };
  int e, n;
  unsigned long long w;
  if (c < 0200) return c;
  e = kTpEnc[bsr(c) - 7];
  n = e & 0xff;
  w = 0;
  do {
    w |= 0200 | (c & 077);
    w <<= 8;
    c >>= 6;
  } while (--n);
  return c | w | e >> 8;
}

/**
 * Writes wide character to stream.
 *
 * @return wc if written or -1 w/ errno
 */
wint_t fputwc(wchar_t wc, FILE *f) {
  unsigned long long w;
  if (wc != -1) {
    w = tpenc(wc);
    do {
      if (fputc(w, f) == -1) {
        return -1;
      }
    } while ((w >>= 8));
    return wc;
  } else {
    return -1;
  }
}
