/*
 * libbrlapi - A library providing access to braille terminals for applications.
 *
 * Copyright (C) 2002-2021 by
 *   Samuel Thibault <Samuel.Thibault@ens-lyon.org>
 *   Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>
 *
 * libbrlapi 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>.
 */

/* api_common.h - private definitions shared by both server & client */

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

#ifdef __MINGW32__
#include <io.h>
#else /* __MINGW32__ */
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif /* __MINGW32__ */

#include "brlapi_protocol.h"

#if !defined(AF_LOCAL) && defined(AF_UNIX)
#define AF_LOCAL AF_UNIX
#endif /* !defined(AF_LOCAL) && defined(AF_UNIX) */

#if !defined(PF_LOCAL) && defined(PF_UNIX)
#define PF_LOCAL PF_UNIX
#endif /* !defined(PF_LOCAL) && defined(PF_UNIX) */

#ifndef MIN
#define MIN(a, b) (((a) < (b))? (a): (b))
#endif /* MIN */

#ifndef MAX
#define MAX(a, b) (((a) > (b))? (a): (b))
#endif /* MAX */

#ifdef __MINGW32__
#define get_osfhandle(fd) _get_osfhandle(fd)
#endif /* __MINGW32__ */

#define LibcError(function) \
  brlapi_errno=BRLAPI_ERROR_LIBCERR; \
  brlapi_libcerrno = errno; \
  brlapi_errfun = function;

/* brlapi_writeFile */
/* Writes a buffer to a file */
static ssize_t brlapi_writeFile(brlapi_fileDescriptor fd, const void *buffer, size_t size)
{
  const unsigned char *buf = buffer;
  size_t n;
#ifdef __MINGW32__
  DWORD res=0;
#else /* __MINGW32__ */
  ssize_t res=0;
#endif /* __MINGW32__ */
  for (n=0;n<size;n+=res) {
#ifdef __MINGW32__
    OVERLAPPED overl = {0, 0, {{0, 0}}, CreateEvent(NULL, TRUE, FALSE, NULL)};
    if ((!WriteFile(fd,buf+n,size-n,&res,&overl)
      && GetLastError() != ERROR_IO_PENDING) ||
      !GetOverlappedResult(fd, &overl, &res, TRUE)) {
      res = GetLastError();
      CloseHandle(overl.hEvent);
      setErrno(res);
      return -1;
    }
    CloseHandle(overl.hEvent);
#else /* __MINGW32__ */
    res=send(fd,buf+n,size-n,0);
    if ((res<0) &&
        (errno!=EINTR) &&
#ifdef EWOULDBLOCK
        (errno!=EWOULDBLOCK) &&
#endif /* EWOULDBLOCK */
        (errno!=EAGAIN)) { /* EAGAIN shouldn't happen, but who knows... */
      return res;
    }
#endif /* __MINGW32__ */
  }
  return n;
}

/* brlapi_readFile */
/* Reads a buffer from a file */
static ssize_t brlapi_readFile(brlapi_fileDescriptor fd, void *buffer, size_t size, int loop)
{
  unsigned char *buf = buffer;
  size_t n;
#ifdef __MINGW32__
  DWORD res=0;
#else /* __MINGW32__ */
  ssize_t res=0;
#endif /* __MINGW32__ */
  for (n=0;n<size && res>=0;n+=res) {
#ifdef __MINGW32__
    OVERLAPPED overl = {0, 0, {{0, 0}}, CreateEvent(NULL, TRUE, FALSE, NULL)};
    if ((!ReadFile(fd,buf+n,size-n,&res,&overl)
      && GetLastError() != ERROR_IO_PENDING) ||
      !GetOverlappedResult(fd, &overl, &res, TRUE)) {
      res = GetLastError();
      CloseHandle(overl.hEvent);
      if (res == ERROR_HANDLE_EOF) return n;
      setErrno(res);
      return -1;
    }
    CloseHandle(overl.hEvent);
#else /* __MINGW32__ */
    res=read(fd,buf+n,size-n);
    if (res<0) {
      if ((errno!=EINTR) &&
#ifdef EWOULDBLOCK
        (errno!=EWOULDBLOCK) &&
#endif /* EWOULDBLOCK */
        (errno!=EAGAIN)) { /* EAGAIN shouldn't happen, but who knows... */
	return -1;
      }
      if (!loop && !n) return -1; /* Nothing read yet, report EINTR */
      /* else, continue reading */
    }
#endif /* __MINGW32__ */
    if (res==0)
      /* Unexpected end of file ! */
      break;
  }
  return n;
}

typedef enum {
#ifdef __MINGW32__
  READY, /* but no pending ReadFile */
#endif /* __MINGW32__ */
  READING_HEADER,
  READING_CONTENT,
  DISCARDING
} PacketState;

typedef struct {
  brlapi_header_t header;
  uint32_t content[BRLAPI_MAXPACKETSIZE/sizeof(uint32_t)+1]; /* +1 for additional \0 */
  PacketState state;
  int readBytes; /* Already read bytes */
  unsigned char *p; /* Where read() should load datas */
  int n; /* Value to give so read() */
#ifdef __MINGW32__
  OVERLAPPED overl;
#endif /* __MINGW32__ */
} Packet;

/* Function: brlapi_resetPacket */
/* Resets a Packet structure */
static void brlapi_resetPacket(Packet *packet)
{
#ifdef __MINGW32__
  packet->state = READY;
#else /* __MINGW32__ */
  packet->state = READING_HEADER;
#endif /* __MINGW32__ */
  packet->readBytes = 0;
  packet->p = (unsigned char *) &packet->header;
  packet->n = sizeof(packet->header);
#ifdef __MINGW32__
  SetEvent(packet->overl.hEvent);
#endif /* __MINGW32__ */
}

/* Function: brlapi_initializePacket */
/* Prepares a Packet structure */
/* returns 0 on success, -1 on failure */
static int brlapi_initializePacket(Packet *packet)
{
#ifdef __MINGW32__
  memset(&packet->overl,0,sizeof(packet->overl));
  if (!(packet->overl.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL))) {
    setSystemErrno();
    LibcError("CreateEvent for readPacket");
    return -1;
  }
#endif /* __MINGW32__ */
  brlapi_resetPacket(packet);
  return 0;
}

/* Function : readPacket */
/* Reads a packet for the given connection */
/* Returns -2 on EOF, -1 on error, 0 if the reading is not complete, */
/* 1 if the packet has been read. */
static int brlapi__readPacket(Packet *packet, brlapi_fileDescriptor descriptor)
{
#ifdef __MINGW32__
  DWORD res;
  if (packet->state!=READY) {
    /* pending read */
    if (!GetOverlappedResult(descriptor,&packet->overl,&res,FALSE)) {
      switch (GetLastError()) {
        case ERROR_IO_PENDING: return 0;
        case ERROR_HANDLE_EOF:
        case ERROR_BROKEN_PIPE: return -2;
        default: setSystemErrno(); LibcError("GetOverlappedResult"); return -1;
      }
    }
read:
#else /* __MINGW32__ */
  int res;
read:
  res = read(descriptor, packet->p, packet->n);
  if (res==-1) {
    switch (errno) {
      case EINTR: goto read;
      case EAGAIN: return 0;
      default: return -1;
    }
  }
#endif /* __MINGW32__ */
  if (res==0) return -2; /* EOF */
  packet->readBytes += res;
  if ((packet->state==READING_HEADER) && (packet->readBytes==BRLAPI_HEADERSIZE)) {
    packet->header.size = ntohl(packet->header.size);
    packet->header.type = ntohl(packet->header.type);
    if (packet->header.size==0) goto out;
    packet->readBytes = 0;
    if (packet->header.size<=BRLAPI_MAXPACKETSIZE) {
      packet->state = READING_CONTENT;
      packet->n = packet->header.size;
    } else {
      packet->state = DISCARDING;
      packet->n = BRLAPI_MAXPACKETSIZE;
    }
    packet->p = (unsigned char*) packet->content;
  } else if ((packet->state == READING_CONTENT) && (packet->readBytes==packet->header.size)) goto out;
  else if (packet->state==DISCARDING) {
    packet->p = (unsigned char *) packet->content;
    packet->n = MIN(packet->header.size-packet->readBytes, BRLAPI_MAXPACKETSIZE);
  } else {
    packet->n -= res;
    packet->p += res;
  }
#ifdef __MINGW32__
  } else packet->state = READING_HEADER;
  if (!ResetEvent(packet->overl.hEvent))
  {
    setSystemErrno();
    LibcError("ResetEvent in readPacket");
  }
  if (!ReadFile(descriptor, packet->p, packet->n, &res, &packet->overl)) {
    switch (GetLastError()) {
      case ERROR_IO_PENDING: return 0;
      case ERROR_HANDLE_EOF:
      case ERROR_BROKEN_PIPE: return -2;
      default: setSystemErrno(); LibcError("ReadFile"); return -1;
    }
  }
#endif /* __MINGW32__ */
  goto read;

out:
  brlapi_resetPacket(packet);
  return 1;
}

/* brlapi_writePacket */
/* Write a packet on the socket */
ssize_t BRLAPI(writePacket)(brlapi_fileDescriptor fd, brlapi_packetType_t type, const void *buf, size_t size)
{
  uint32_t header[2] = { htonl(size), htonl(type) };
  ssize_t res;

  /* first send packet header (size+type) */
  if ((res=brlapi_writeFile(fd,&header[0],sizeof(header)))<0) {
    LibcError("write in writePacket");
    return res;
  }

  /* eventually data */
  if (size && buf)
    if ((res=brlapi_writeFile(fd,buf,size))<0) {
      LibcError("write in writePacket");
      return res;
    }

  return 0;
}

/* brlapi_readPacketHeader */
/* Read a packet's header and return packet's size */
ssize_t BRLAPI(readPacketHeader)(brlapi_fileDescriptor fd, brlapi_packetType_t *packetType)
{
  uint32_t header[2];
  ssize_t res;
  if ((res=brlapi_readFile(fd,header,sizeof(header),0)) != sizeof(header)) {
    if (res<0) {
      /* reports EINTR too */
      LibcError("read in brlapi_readPacketHeader");
      return -1;
    } else return -2;
  }
  *packetType = ntohl(header[1]);
  return ntohl(header[0]);
}

/* brlapi_readPacketContent */
/* Read a packet's content into the given buffer */
/* If the packet is too large, the buffer is filled with the */
/* beginning of the packet, the rest of the packet being discarded */
/* Returns packet size, -1 on failure, -2 on EOF */
ssize_t BRLAPI(readPacketContent)(brlapi_fileDescriptor fd, size_t packetSize, void *buf, size_t bufSize)
{
  ssize_t res;
  char foo[BRLAPI_MAXPACKETSIZE];
  while (1) {
    res = brlapi_readFile(fd,buf,MIN(bufSize,packetSize),1);
    if (res >= 0) break;
    if (errno != EINTR
#ifdef EWOULDBLOCK
	&& errno != EWOULDBLOCK
#endif /* EWOULDBLOCK */
	&& errno != EAGAIN)
      goto out;
  }
  if (res<MIN(bufSize,packetSize)) return -2; /* pkt smaller than announced => EOF */
  if (packetSize>bufSize) {
    size_t discard = packetSize-bufSize;
    for (res=0; res<discard / sizeof(foo); res++)
      brlapi_readFile(fd,foo,sizeof(foo),1);
    brlapi_readFile(fd,foo,discard % sizeof(foo),1);
  }
  return packetSize;

out:
  LibcError("read in brlapi_readPacket");
  return -1;
}

/* brlapi_readPacket */
/* Read a packet */
/* Returns packet's size, -2 if EOF, -1 on error */
/* If the packet is larger than the supplied buffer, then */
/* the packet is truncated to buffer's size, like in the recv system call */
/* with option MSG_TRUNC (rest of the pcket is read but discarded) */
ssize_t BRLAPI(readPacket)(brlapi_fileDescriptor fd, brlapi_packetType_t *packetType, void *buf, size_t size)
{
  ssize_t res = BRLAPI(readPacketHeader)(fd, packetType);
  if (res<0) return res; /* reports EINTR too */
  return BRLAPI(readPacketContent)(fd, res, buf, size);
}

/* Function : brlapi_loadAuthKey */
/* Loads an authorization key from the given file */
/* It is stored in auth, and its size in authLength */
/* If the file is non-existant or unreadable, returns -1 */
static int BRLAPI(loadAuthKey)(const char *filename, size_t *authlength, void *auth)
{
  int fd;
  off_t stsize;
  struct stat statbuf;
  if (stat(filename, &statbuf)<0) {
    LibcError("stat in loadAuthKey");
    return -1;
  }

  if (statbuf.st_size==0) {
    brlapi_errno = BRLAPI_ERROR_EMPTYKEY;
    brlapi_errfun = "brlapi_laudAuthKey";
    return -1;
  }

  stsize = MIN(statbuf.st_size, BRLAPI_MAXPACKETSIZE-2*sizeof(uint32_t));

  if ((fd = open(filename, O_RDONLY)) <0) {
    LibcError("open in loadAuthKey");
    return -1;
  }

  *authlength = brlapi_readFile(
#ifdef __MINGW32__
		  (HANDLE) get_osfhandle(fd),
#else /* __MINGW32__ */
		  fd,
#endif /* __MINGW32__ */
		  auth, stsize, 1);

  if (*authlength!=(size_t)stsize) {
    LibcError("read in loadAuthKey");
    close(fd);
    return -1;
  }

  close(fd);
  return 0;
}

static char *
intToString (int64_t value) {
  char buffer[0X20];
  snprintf(buffer, sizeof(buffer), "%"PRId64, value);
  return strdup(buffer);
}

#define LOCALHOST_ADDRESS_IPV4 "127.0.0.1"
#define LOCALHOST_ADDRESS_IPV6 "::1"

static int
isPortNumber (const char *number, uint16_t *port) {
  if (!number) return 0;
  if (*number < '0') return 0;
  if (*number > '9') return 0;

  char *end;
  long int value = strtol(number, &end, 10);
  if (*end) return 0;

  if (value < 0) return 0;
  if (value > (UINT16_MAX - BRLAPI_SOCKETPORTNUM)) return 0;

  if (port) *port = value + BRLAPI_SOCKETPORTNUM;
  return 1;
}

/* Function: brlapi_expandHost
 * splits host into host & port */
static int BRLAPI(expandHost)(const char *hostAndPort, char **host, char **port) {
  const char *c;
  if (!hostAndPort || !*hostAndPort) {
#if defined(PF_LOCAL)
    *host = NULL;
    *port = strdup("0");
    return PF_LOCAL;
#else /* PF_LOCAL */
    *host = strdup(LOCALHOST_ADDRESS_IPV4);
    *port = strdup(BRLAPI_SOCKETPORT);
    return PF_UNSPEC;
#endif /* PF_LOCAL */
  } else if ((c = strrchr(hostAndPort,':'))) {
    if (c != hostAndPort) {
      uint16_t porti = BRLAPI_SOCKETPORTNUM;
      isPortNumber(c+1, &porti);
      *host = malloc(c-hostAndPort+1);
      memcpy(*host, hostAndPort, c-hostAndPort);
      (*host)[c-hostAndPort] = 0;
      *port = intToString(porti);
      return PF_UNSPEC;
    } else {
#if defined(PF_LOCAL)
      *host = NULL;
      *port = strdup(c+1);
      return PF_LOCAL;
#else /* PF_LOCAL */
      uint16_t porti = BRLAPI_SOCKETPORTNUM;
      isPortNumber(c+1, &porti);
      *host = strdup(LOCALHOST_ADDRESS_IPV4);
      *port = intToString(porti);
      return PF_UNSPEC;
#endif /* PF_LOCAL */
    }
  } else {
    *host = strdup(hostAndPort);
    *port = strdup(BRLAPI_SOCKETPORT);
    return PF_UNSPEC;
  }
}

typedef struct {
  brlapi_packetType_t type;
  const char *name;
} brlapi_packetTypeEntry_t;

static const brlapi_packetTypeEntry_t brlapi_packetTypeTable[] = {
  { BRLAPI_PACKET_VERSION, "Version" },
  { BRLAPI_PACKET_AUTH, "Auth" },
  { BRLAPI_PACKET_GETDRIVERNAME, "GetDriverName" },
  { BRLAPI_PACKET_GETDISPLAYSIZE, "GetDisplaySize" },
  { BRLAPI_PACKET_ENTERTTYMODE, "EnterTtyMode" },
  { BRLAPI_PACKET_SETFOCUS, "SetFocus" },
  { BRLAPI_PACKET_LEAVETTYMODE, "LeaveTtyMode" },
  { BRLAPI_PACKET_KEY, "Key" },
  { BRLAPI_PACKET_IGNOREKEYRANGES, "IgnoreKeyRanges" },
  { BRLAPI_PACKET_ACCEPTKEYRANGES, "AcceptKeyRanges" },
  { BRLAPI_PACKET_WRITE, "Write" },
  { BRLAPI_PACKET_ENTERRAWMODE, "EnterRawMode" },
  { BRLAPI_PACKET_LEAVERAWMODE, "LeaveRawMode" },
  { BRLAPI_PACKET_PACKET, "Packet" },
  { BRLAPI_PACKET_SUSPENDDRIVER, "SuspendDriver" },
  { BRLAPI_PACKET_RESUMEDRIVER, "ResumeDriver" },
  { BRLAPI_PACKET_PARAM_VALUE, "ParameterValue" },
  { BRLAPI_PACKET_PARAM_REQUEST, "ParameterRequest" },
  { BRLAPI_PACKET_SYNCHRONIZE, "Synchronize" },
  { BRLAPI_PACKET_ACK, "Ack" },
  { BRLAPI_PACKET_ERROR, "Error" },
  { BRLAPI_PACKET_EXCEPTION, "Exception" },
  { 0, NULL }
};

const char * BRLAPI_STDCALL BRLAPI(getPacketTypeName)(brlapi_packetType_t type)
{
  const brlapi_packetTypeEntry_t *p;
  for (p = brlapi_packetTypeTable; p->type; p++)
    if (type==p->type) return p->name;
  return "Unknown";
}

static int
BRLAPI(getArgumentWidth) (brlapi_keyCode_t keyCode) {
  brlapi_keyCode_t code = keyCode & BRLAPI_KEY_CODE_MASK;

  switch (keyCode & BRLAPI_KEY_TYPE_MASK) {
    default: break;

    case BRLAPI_KEY_TYPE_SYM:
      switch (code & 0XFF000000U) {
        default: break;

        case 0X00000000U:
          switch (code & 0XFF0000U) {
            default: break;
            case 0X000000U: return 8;
          }
          break;

        case 0X01000000U: return 24;
      }
      break;

    case BRLAPI_KEY_TYPE_CMD:
      switch (code & BRLAPI_KEY_CMD_BLK_MASK) {
        default: return 16;
        case 0: return 0;
      }
      break;
  }

  brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
  return -1;
}

/* Function brlapi_uint32ToKeyCode */
/* Translate keycodes stored in a packet to a keycode */
static brlapi_keyCode_t
BRLAPI(packetToKeyCode)(uint32_t t[2])
{
  return (((brlapi_keyCode_t)ntohl(t[0])) << 32) | ntohl(t[1]);
}

/* Function : brlapi_getKeyrangeMask */
/* returns the keyCode mask for a given range type */
static int
BRLAPI(getKeyrangeMask) (brlapi_rangeType_t r, brlapi_keyCode_t code, brlapi_keyCode_t *mask)
{
  switch(r) {
    case brlapi_rangeType_all:
      *mask = BRLAPI_KEY_MAX;
      return 0;
    case brlapi_rangeType_type:
      *mask = BRLAPI_KEY_CODE_MASK|BRLAPI_KEY_FLAGS_MASK;
      return 0;
    case brlapi_rangeType_command: {
      int width = BRLAPI(getArgumentWidth)(code);
      if (width == -1) return -1;
      *mask = ((1 << width) - 1) | BRLAPI_KEY_FLAGS_MASK;
      return 0;
    }
    case brlapi_rangeType_key:
      *mask = BRLAPI_KEY_FLAGS_MASK;
      return 0;
    case brlapi_rangeType_code:
      *mask = 0;
      return 0;
  }
  brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
  return -1;
}

static char *
BRLAPI(getKeyFile)(const char *auth)
{
  const char *path;
  char *ret, *delim;
  if (!strncmp(auth,"keyfile:",8))
    path=auth+8;
  else {
    path=strstr(auth,"+keyfile:");
    if (path) path+=9;
    else path=auth;
  }
  ret=strdup(path);
  delim=strchr(ret,'+');
  if (delim)
    *delim = 0;
  return ret;
}

static const brlapi_param_properties_t brlapi_param_properties[BRLAPI_PARAM_COUNT] = {
//Connection Parameters
  [BRLAPI_PARAM_SERVER_VERSION] = {
    .type = BRLAPI_PARAM_TYPE_UINT32,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_CLIENT_PRIORITY] = {
    .type = BRLAPI_PARAM_TYPE_UINT32,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

//Device Parameters
  [BRLAPI_PARAM_DRIVER_NAME] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DRIVER_CODE] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DRIVER_VERSION] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DEVICE_MODEL] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DEVICE_CELL_SIZE] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DISPLAY_SIZE] = {
    .type = BRLAPI_PARAM_TYPE_UINT32,
    .canRead = 1,
    .canWatch = 1,
    .isArray = 1,
    .arraySize = 2,
  },

  [BRLAPI_PARAM_DEVICE_IDENTIFIER] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DEVICE_SPEED] = {
    .type = BRLAPI_PARAM_TYPE_UINT32,
    .canRead = 1,
    .canWatch = 1,
  },

  [BRLAPI_PARAM_DEVICE_ONLINE] = {
    .type = BRLAPI_PARAM_TYPE_BOOLEAN,
    .canRead = 1,
    .canWatch = 1,
  },

//Input Parameters
  [BRLAPI_PARAM_RETAIN_DOTS] = {
    .type = BRLAPI_PARAM_TYPE_BOOLEAN,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

//Braille Rendering Parameters
  [BRLAPI_PARAM_COMPUTER_BRAILLE_CELL_SIZE] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_LITERARY_BRAILLE] = {
    .type = BRLAPI_PARAM_TYPE_BOOLEAN,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_CURSOR_DOTS] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_CURSOR_BLINK_PERIOD] = {
    .type = BRLAPI_PARAM_TYPE_UINT32,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_CURSOR_BLINK_PERCENTAGE] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_RENDERED_CELLS] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .canWatch = 1,
    .isArray = 1,
  },

//Navigation Parameters
  [BRLAPI_PARAM_SKIP_IDENTICAL_LINES] = {
    .type = BRLAPI_PARAM_TYPE_BOOLEAN,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_AUDIBLE_ALERTS] = {
    .type = BRLAPI_PARAM_TYPE_BOOLEAN,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

//Clipboard Parameters
  [BRLAPI_PARAM_CLIPBOARD_CONTENT] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

//TTY Mode Parameters
  [BRLAPI_PARAM_BOUND_COMMAND_KEYCODES] = {
    .type = BRLAPI_PARAM_TYPE_KEYCODE,
    .canRead = 1,
    .canWatch = 1,
    .isArray = 1,
  },

  [BRLAPI_PARAM_COMMAND_KEYCODE_NAME] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .hasSubparam = 1,
  },

  [BRLAPI_PARAM_COMMAND_KEYCODE_SUMMARY] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .hasSubparam = 1,
  },

  [BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES] = {
    .type = BRLAPI_PARAM_TYPE_KEYCODE,
    .canRead = 1,
    .canWatch = 1,
    .isArray = 1,
  },

  [BRLAPI_PARAM_DRIVER_KEYCODE_NAME] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .hasSubparam = 1,
  },

  [BRLAPI_PARAM_DRIVER_KEYCODE_SUMMARY] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .hasSubparam = 1,
  },

//Braille Translation Parameters
  [BRLAPI_PARAM_COMPUTER_BRAILLE_ROWS_MASK] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .isArray = 1,
    .arraySize = (0X10FFFF + 1) / 0X100 / 8,
  },

  [BRLAPI_PARAM_COMPUTER_BRAILLE_ROW_CELLS] = {
    .type = BRLAPI_PARAM_TYPE_UINT8,
    .canRead = 1,
    .isArray = 1,
    .arraySize = 0X100 + (0X100 / 8),
    .hasSubparam = 1,
  },

  [BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_LITERARY_BRAILLE_TABLE] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },

  [BRLAPI_PARAM_MESSAGE_LOCALE] = {
    .type = BRLAPI_PARAM_TYPE_STRING,
    .canRead = 1,
    .canWatch = 1,
    .canWrite = 1,
  },
};

const brlapi_param_properties_t *brlapi_getParameterProperties(brlapi_param_t parameter) {
  if (parameter >= (sizeof(brlapi_param_properties) / sizeof(*brlapi_param_properties)))
    return NULL;

  return &brlapi_param_properties[parameter];
}

typedef void IntegerConverter (void *v);

static void convertInteger_hton16 (void *v) {
  uint16_t *u = v;
  *u = htons(*u);
}

static void convertInteger_ntoh16 (void *v) {
  uint16_t *u = v;
  *u = ntohs(*u);
}

static void convertInteger_hton32 (void *v) {
  uint32_t *u = v;
  *u = htonl(*u);
}

static void convertInteger_ntoh32 (void *v) {
  uint32_t *u = v;
  *u = ntohl(*u);
}

#define UINT64_SHIFT ((sizeof(uint64_t) / 2) * 8)
#define UINT64_LOW ((UINT64_C(1) << UINT64_SHIFT) - 1)

static void convertInteger_hton64 (void *v) {
#ifndef WORDS_BIGENDIAN
  uint64_t *u = v;
  uint32_t low = *u & UINT64_LOW;
  uint32_t high = *u >> UINT64_SHIFT;
  *u = ((uint64_t)htonl(low) << UINT64_SHIFT) | htonl(high);
#endif /* WORDS_BIGENDIAN */
}

static void convertInteger_ntoh64 (void *v) {
#ifndef WORDS_BIGENDIAN
  uint64_t *u = v;
  uint32_t low = *u & UINT64_LOW;
  uint32_t high = *u >> UINT64_SHIFT;
  *u = ((uint64_t)ntohl(low) << UINT64_SHIFT) | ntohl(high);
#endif /* WORDS_BIGENDIAN */
}

static void convertIntegers (void *data, size_t length, size_t size, IntegerConverter *convert) {
  length /= size;
  length *= size;
  void *end = data + length;

  while (data < end) {
    convert(data);
    data += size;
  }
}

/* Function: _brlapi_htonParameter */
/* swap uint32 values of the parameter from host to network */
void _brlapi_htonParameter(brlapi_param_t parameter, brlapi_paramValuePacket_t *value, size_t len)
{
  const brlapi_param_properties_t *properties = brlapi_getParameterProperties(parameter);

  if (properties) {
    void *p =  value->data;

    switch (properties->type) {
      case BRLAPI_PARAM_TYPE_UINT16:
        convertIntegers(p, len, sizeof(uint16_t), convertInteger_hton16);
        return;

      case BRLAPI_PARAM_TYPE_UINT32:
        convertIntegers(p, len, sizeof(uint32_t), convertInteger_hton32);
        return;

      case BRLAPI_PARAM_TYPE_UINT64:
        convertIntegers(p, len, sizeof(uint64_t), convertInteger_hton64);
        return;

      default:
        /* no conversion needed */
        return;
    }
  }
}

/* Function: _brlapi_ntohParameter */
/* swap uint32 values of the parameter from network to host */
void _brlapi_ntohParameter(brlapi_param_t parameter, brlapi_paramValuePacket_t *value, size_t len)
{
  const brlapi_param_properties_t *properties = brlapi_getParameterProperties(parameter);

  if (properties) {
    void *p =  value->data;

    switch (properties->type) {
      case BRLAPI_PARAM_TYPE_UINT16:
        convertIntegers(p, len, sizeof(uint16_t), convertInteger_ntoh16);
        return;

      case BRLAPI_PARAM_TYPE_UINT32:
        convertIntegers(p, len, sizeof(uint32_t), convertInteger_ntoh32);
        return;

      case BRLAPI_PARAM_TYPE_UINT64:
        convertIntegers(p, len, sizeof(uint64_t), convertInteger_ntoh64);
        return;

      default:
        /* no conversion needed */
        return;
    }
  }
}

