/*
 * BRLTTY - A background process providing access to the console screen (when in
 *          text mode) for a blind person using a refreshable braille display.
 *
 * Copyright (C) 1995-2023 by The BRLTTY Developers.
 *
 * BRLTTY comes with ABSOLUTELY NO WARRANTY.
 *
 * This is free software, placed under the terms of the
 * GNU Lesser General Public License, as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any
 * later version. Please see the file LICENSE-LGPL for details.
 *
 * Web Page: http://brltty.app/
 *
 * This software is maintained by Dave Mielke <dave@mielke.cc>.
 */

#ifndef BRLTTY_INCLUDED_BRL_DOTS
#define BRLTTY_INCLUDED_BRL_DOTS

#include <strings.h>

#ifdef __MINGW32__
extern int ffs (int i);
#endif /* __MINGW32__ */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* The bits for each braille dot as defined by the ISO 11548-1 standard.
 *
 * From least- to most-significant octal digit:
 * +  the first contains dots 1-3
 * +  the second contains dots 4-6
 * +  the third contains dots 7-8
 * 
 * Here are a few ways to illustrate a braille cell:
 *    By Dot   By Bit   As Octal
 *    Number   Number    Digits
 *     1  4     0  3    001  010
 *     2  5     1  4    002  020
 *     3  6     2  5    004  040
 *     7  8     6  7    100  200
 */

typedef unsigned char BrlDots;

#define BRL_DOT_COUNT 8

#define BRL_DOT(number) (BrlDots)(1 << ((number) - 1))
#define BRL_DOT_1 BRL_DOT(1) /* upper-left dot of standard braille cell */
#define BRL_DOT_2 BRL_DOT(2) /* middle-left dot of standard braille cell */
#define BRL_DOT_3 BRL_DOT(3) /* lower-left dot of standard braille cell */
#define BRL_DOT_4 BRL_DOT(4) /* upper-right dot of standard braille cell */
#define BRL_DOT_5 BRL_DOT(5) /* middle-right dot of standard braille cell */
#define BRL_DOT_6 BRL_DOT(6) /* lower-right dot of standard braille cell */
#define BRL_DOT_7 BRL_DOT(7) /* lower-left dot of computer braille cell */
#define BRL_DOT_8 BRL_DOT(8) /* lower-right dot of computer braille cell */

#define BRL_DOTS_LEFT (BRL_DOT_1 | BRL_DOT_2 | BRL_DOT_3 | BRL_DOT_7)
#define BRL_DOTS_RIGHT (BRL_DOT_4 | BRL_DOT_5 | BRL_DOT_6 | BRL_DOT_8)
#define BRL_DOTS_ALL (BRL_DOTS_LEFT | BRL_DOTS_RIGHT)

static inline BrlDots
brlGetLeftDots (BrlDots cell) {
  return cell & BRL_DOTS_LEFT;
}

static inline BrlDots
brlGetRightDots (BrlDots cell) {
  return cell & BRL_DOTS_RIGHT;
}

static inline BrlDots
brlGetLeftDotsToRightDots (BrlDots cell) {
  BrlDots dots = 0;
  if (cell & BRL_DOT_1) dots |= BRL_DOT_4;
  if (cell & BRL_DOT_2) dots |= BRL_DOT_5;
  if (cell & BRL_DOT_3) dots |= BRL_DOT_6;
  if (cell & BRL_DOT_7) dots |= BRL_DOT_8;
  return dots;
}

static inline BrlDots
brlGetRightDotsToLeftDots (BrlDots cell) {
  BrlDots dots = 0;
  if (cell & BRL_DOT_4) dots |= BRL_DOT_1;
  if (cell & BRL_DOT_5) dots |= BRL_DOT_2;
  if (cell & BRL_DOT_6) dots |= BRL_DOT_3;
  if (cell & BRL_DOT_8) dots |= BRL_DOT_7;
  return dots;
}

static inline BrlDots
brlGetLeftDotsToRightDotsAlt (BrlDots cell) {
  BrlDots dots = 0;
  if (cell & BRL_DOT_1) dots |= BRL_DOT_6;
  if (cell & BRL_DOT_2) dots |= BRL_DOT_5;
  if (cell & BRL_DOT_3) dots |= BRL_DOT_4;
  if (cell & BRL_DOT_7) dots |= BRL_DOT_8;
  return dots;
}

static inline BrlDots
brlGetRightDotsToLeftDotsAlt (BrlDots cell) {
  BrlDots dots = 0;
  if (cell & BRL_DOT_4) dots |= BRL_DOT_3;
  if (cell & BRL_DOT_5) dots |= BRL_DOT_2;
  if (cell & BRL_DOT_6) dots |= BRL_DOT_1;
  if (cell & BRL_DOT_8) dots |= BRL_DOT_7;
  return dots;
}

static inline BrlDots
brlNumberToDot (char number) {
  return ((number >= '1') && (number <= '8'))? BRL_DOT(number - '0'): 0;
}

static inline char
brlDotToNumber (BrlDots dot) {
  int shift = ffs(dot);
  return shift? ((char)shift + '0'): 0;
}

static inline void
brlRemapDot (BrlDots *dots, BrlDots from, BrlDots to) {
  if (*dots & from) {
    *dots &= ~from;
    *dots |= to;
  }
}

typedef BrlDots BrlDotTable[BRL_DOT_COUNT];

typedef char BrlDotNumbersBuffer[BRL_DOT_COUNT + 1];

static inline unsigned int
brlDotsToNumbers (BrlDots dots, BrlDotNumbersBuffer numbers) {
  char *number = numbers;

  while (dots) {
    int shift = ffs(dots) - 1;
    dots -= 1 << shift;
    *number++ = (char)shift + '1';
  }

  *number = 0;
  return (unsigned int)(number - numbers);
}

/* The Unicode row used for literal braille dot representations. */
#define BRL_UNICODE_ROW 0X2800

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* BRLTTY_INCLUDED_BRL_DOTS */
