/*                                              -*- mode:C++; fill-column:100 -*-
  SFBPrint.cpp - Abstracted formatted printing support
  Copyright (C) 2009 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it 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.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA

  $Id$
*/

#include "SFBPrint.h"
#include "SFBAssert.h"
#include "SFBReflex.h"

void print(const char * str) {         facePrint(ALL_FACES,str); }
void print(const u8 * str, u32 len) {  facePrint(ALL_FACES,str,len); }
void print(int decimal) {              facePrint(ALL_FACES,decimal); }
void print(unsigned int decimal) {     facePrint(ALL_FACES,decimal); }
void print(long decimal) {             facePrint(ALL_FACES,decimal); }
void print(unsigned long decimal) {    facePrint(ALL_FACES,decimal); }
void print(long decimal, int code) {   facePrint(ALL_FACES,decimal,code); }
void print(double val) {               facePrint(ALL_FACES,val); }
void printFace() {                     facePrintFace(ALL_FACES); }

void println() {                       facePrintln(ALL_FACES); }
void println(const char * str) {       facePrintln(ALL_FACES,str); }
void println(const u8 * str, u32 len) {facePrintln(ALL_FACES,str,len); }
void println(int decimal) {            facePrintln(ALL_FACES,decimal); }
void println(unsigned int decimal) {   facePrintln(ALL_FACES,decimal); }
void println(long decimal) {           facePrintln(ALL_FACES,decimal); }
void println(unsigned long decimal) {  facePrintln(ALL_FACES,decimal); }
void println(long decimal, int code) { facePrintln(ALL_FACES,decimal,code); }
void println(double val) {             facePrintln(ALL_FACES,val); }

const FacePrinter *(facePrinters[MAX_FACE_INDEX]) IN_BODYRAM_BSS;

static u8 faceCheckBytes[MAX_FACE_INDEX] IN_BODYRAM_BSS;

bool faceFindFreeFace(u8 & foundFace) {
  for (u8 f = MIN_VIRTUAL_FACE; f < MAX_FACE_INDEX; ++f) {
    if (!facePrinters[f]) {
      foundFace = f;
      return true;
    }
  }
  return false;
}

static void packetPrinterPrint(u8 face, u8 byte) {
  PacketPrinterHeader * pph = (PacketPrinterHeader*) faceGetPrinter(face);
  API_ASSERT(pph,E_BUG_INCONSISTENT_STATE); // Shouldn't be able to get here otherwise

  u8 * packetBuffer = ((u8 *) &pph->sourceFace)+1;
  u8 * packet = packetBuffer+4;
  PacketHeader & ph = packetHeaderInternalUnsafe(packet);

  if (ph.flags)                 // If already blown, we're dumping
    return;

  if (pph->usedLen == 0) {      // First byte of new packet?
    ph.source = pph->sourceFace; // Then reset the header
    ph.length = 0;
    ph.cursor = 0;
    ph.flags = 0;
  }

  if (pph->usedLen < pph->maxLen) {
    packet[pph->usedLen++] = byte;
    packet[pph->usedLen] = 0;   // Flying null terminator..
    ph.length = pph->usedLen;
  } else
    ph.flags |= PK_BUFFER;
}

static void packetPrinterTerpri(u8 face) {
  PacketPrinterHeader * pph = (PacketPrinterHeader*) faceGetPrinter(face);
  API_ASSERT(pph,E_BUG_INCONSISTENT_STATE); // Shouldn't be able to get here otherwise

  // We keep the packet is 'ready to ship' condition as we build it.  So not much to do here.
  // Only subtlety is back-to-back println's have to flush any existing packet

  u8 * packetBuffer = ((u8 *) &pph->sourceFace)+1;
  u8 * packet = packetBuffer+4;
  PacketHeader & ph = packetHeaderInternalUnsafe(packet);

  if (pph->usedLen == 0) {      // println after println?
    ph.source = pph->sourceFace; // then reset the header
    ph.length = 0;
    ph.cursor = 0;
    ph.flags = 0;
  } else
    pph->usedLen = 0;           // Next print byte will reset the packet
}

void packetPrinterPrintFaceCode(u8 face) {
  PacketPrinterHeader * pph = (PacketPrinterHeader*) faceGetPrinter(face);
  API_ASSERT(pph,E_BUG_INCONSISTENT_STATE); // Shouldn't be able to get here otherwise
  facePrint(face, "NSEWPMB"[pph->sourceFace]);
}

u8 * makePacketPrinter(u8 & assignedFace, u8 * buffer, u32 bufferLength, u8 sourceFace) {
  API_ASSERT_NONNULL(buffer);
  API_ASSERT_GREATER_EQUAL(bufferLength,PACKET_PRINTER_BUFFER_OVERHEAD_BYTES);
  API_ASSERT_MAX(sourceFace,ALL_FACES); // Legal sources are NSEW, SPINE, WMEM, BRAIN, only

  u8 face;
  if (!faceFindFreeFace(face)) 
    return 0;

  uptr alignedAddress = (((uptr) buffer)+3)&~3; // Word align
  u32 wasted = (u32) (alignedAddress-((uptr) buffer));
  PacketPrinterHeader * pph = (PacketPrinterHeader*) alignedAddress;

  u32 available = bufferLength-sizeof(PacketPrinterHeader)-sizeof(PacketHeader)-wasted-1;

  pph->maxLen = MIN(MAX_PACKET_LENGTH,available);
  pph->sourceFace = sourceFace;

  pph->fp.print = packetPrinterPrint;
  pph->fp.println = packetPrinterTerpri;
  pph->fp.printFaceCode = packetPrinterPrintFaceCode;
  pph->fp.getFaceName = 0;

  u8 * packetBuffer = ((u8 *) &pph->sourceFace)+1;
  u8 * ret = packetBuffer+4;
  PacketHeader & ph = packetHeaderInternalUnsafe(ret);
  ph.source = pph->sourceFace;
  ph.flags = 0;
  ph.cursor = 0;
  ph.length = 0;

  assignedFace = face;

  faceSetPrinter(assignedFace,(FacePrinter*) pph);
  facePrintln(face);

  return ret;
}

bool faceHasPrinter(u8 face) {
  return faceGetPrinter(face)!=0;
}

const FacePrinter * faceGetPrinter(u8 face) {
  if (face >= MAX_FACE_INDEX)
    return 0;
  return facePrinters[face];
}

void faceSetPrinter(u8 face, const FacePrinter * handler) {
  API_ASSERT_MAX(face,MAX_FACE_INDEX);
  API_ASSERT_NONNULL(handler);
  API_ASSERT_ZERO(facePrinters[face]);
  API_ASSERT((handler->print != 0) == (handler->println != 0),E_API_ARGUMENT);  /* Must have both or neither of print + println */
  facePrinters[face] = handler;
  faceCheckBytes[face] = CHECK_BYTE_INIT_VALUE;

  API_ASSERT_EQUAL(faceCheckBytes[face],0xf0); // XXX
}

void faceUnsetPrinter(u8 face) {
  API_ASSERT_MAX(face,MAX_FACE_INDEX);
  facePrinters[face] = 0;
}
const FacePrinter * getHandler(u8 face) {
  if (face >= MAX_FACE_INDEX) return 0;
  return facePrinters[face];
}
static void facePutcImpl(u8 face, u8 byte) {
  CHECK_BYTE_UPDATE(faceCheckBytes[face],byte);
  facePrinters[face]->print(face,byte);
}
static void facePrintlnImpl(u8 face) {
  faceCheckBytes[face] = CHECK_BYTE_INIT_VALUE;
  facePrinters[face]->println(face);
}

/**
   Print the contents of a null-terminated string to the current xmit packet of \a face.  Compare to
   ::facePrint(u8 face, const u8 * str, u32 len).

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param str A pointer to the null-terminate string to append.
 
  \blinks #E_API_NULL_POINTER if \a str is null.

  \sideeffect The currently xmit packet of \a face gets up to \c strlen(str) bytes longer, if packet
              length and buffer space room is available.

  \usage
  \code
    void myHandler(u8 * packet) {
      facePrint(NORTH,"L I received a packet of length "); // Print some text to NorthFace only
      facePrintln(NORTH,packetLength(packet), DEC);        // End packet after a decimal number
    }
  \endcode
 */
void facePrint(u8 face, const char * str) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->print) return;
  u8 ch;
  while ((ch = *str++))
    facePutcImpl(face, ch);
}

/**
   Print \a str, an array of bytes of length \a len, to the current xmit packet of \a face.  Compare
   to ::facePrint(u8 face, const char * str).  If \a str is a packet, you may want to consider the
   '\%p' or '\%\#p' formats of #facePrintf() as well.

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param str A pointer to the byte array to append.  Must be of length (at least) \a len; does not
             need to be null-terminated.
 
  \blinks #E_API_NULL_POINTER if \a str is null.

  \sideeffect The currently xmit packet of \a face gets up to \a len bytes longer, if packet length
              and buffer space room is available.

  \usage
  \code
    void myHandler(u8 * packet) {
      facePrint(NORTH,"L I got a packet containing '");      // Print some text to NorthFace
      facePrint(NORTH,packet,packetLength(packet));          // Add entire contents of packet
      facePrintln(NORTH,"'");                                // Add an end quote then end the packet 
    }
  \endcode
 */
void facePrint(u8 face, const u8 * str, u32 len) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->print) return;
  while (len--)
    facePutcImpl(face,*str++);
}

/**
   Same as #facePrint(u8 face, long decimal).
 */
void facePrint(u8 face, int decimal) {
  facePrint(face, (long) decimal);
}

/**
   Print \a decimal as an unsigned decimal number, to the current xmit packet of \a face.  Compare
   to ::facePrint(u8 face, int decimal).  

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param decimal A 32 bit number to be printed as an unsigned decimal value.
 
  \sideeffect The currently xmit packet of \a face gets up to ten bytes longer, if packet length and
              buffer space room is available.

  \usage
  \code
    void myHandler(u8 *) {
      u32 num = 123*456;             // Compute something. 'u32' means 'unsigned 32 bit value'
      facePrint(EAST,"L answer=");   // Print some text to EastFace
      facePrintln(EAST,num);         // Prints '56088' and ends the packet
    }
  \endcode
 */
void facePrint(u8 face, unsigned int decimal) {
  facePrint(face, (unsigned long) decimal);
}

/**
   Print \a decimal as a signed decimal number, to the current xmit packet of \a face.  Compare to
   ::facePrint(u8 face, unsigned long decimal).  If \a decimal is less than zero, prints a '-'
   followed by the value of \c -decimal (note this gives the wrong output for the 'most negative'
   possible number.)

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param decimal A 32 bit number to be printed as a signed decimal value.
 
  \sideeffect The currently xmit packet of \a face gets up to eleven bytes longer, if packet length
              and buffer space room is available.

  \usage
  \code
    void myHandler(u8 *) {
      long num = 123-456;               // Compute something. 'int' means 'signed 32 bit value'
      facePrint(EAST,"L answer=");     // Print some text to EastFace
      facePrintln(EAST,num);           // Prints '-333' and ends the packet
    }
  \endcode
 */
void facePrint(u8 face, long decimal) {
  if (decimal < 0) {
    facePutcImpl(face, '-');
    decimal = -decimal;
  }
  facePrint(face,(unsigned long) decimal);
}

/**
   Same as #facePrint(u8 face, unsigned long decimal, int code) with \c code set to \c DEC.
 */
void facePrint(u8 face, unsigned long decimal) {
  facePrint(face,decimal,DEC);
}

static void facePrintInBase(u8 face, unsigned long n, int base, bool printneg, int width, bool zerofill) { 
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->print) return;

  if (printneg && ((s32) n) < 0) {
    --width;
    n = -n;
  } else printneg = false;

  unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. 
  int i = 0;

  if (n == 0) 
    buf[i++] = 0;

  while (n > 0) {
    buf[i++] = n % base;
    n /= base;
  }
  
  if (printneg && zerofill)
    facePutcImpl(face,'-');

  while (width-- > i)
    facePutcImpl(face,zerofill?'0':' ');

  if (printneg && !zerofill)
    facePutcImpl(face,'-');

  while (--i >= 0)
    facePutcImpl(face,
                 buf[i] < 10 ?
                 '0' + buf[i] :
                 'A' + buf[i] - 10);
}

/**
   Print \a num, in a format selected by \a code, to the current xmit packet of \a face.  The possible
   format codes are:

   - #DEC: Print one to ten bytes containing the base-10 representation of the 32 bits of \a num,
      using the ASCII characters '0' through '9'.  See the '%d' code in #packetScanf(u8 * packet,
      const char * format,...)  for a way to read in a value that was printed with code \c DEC, and
      in #facePrintf(u8 face, const char * format,...)  for an alternate way to print in this
      representation.
   - #HEX: Print one to eight bytes containing the base-16 representation of the 32 bits of \a num,
      using the ASCII characters '0' through '9' to represent 0 through 9, and 'A' through 'F' to
      represent 10 through 15.  See the '%x' code in #packetScanf(u8 * packet, const char *
      format,...) for a way to read in a value that was printed with code \c HEX, and in
      #facePrintf(u8 face, const char * format,...)  for an alternate way to print in this
      representation.
   - #OCT: Print one to eleven bytes containing the base-8 representation of the 32 bits of \a num,
      using the ASCII characters '0' through '7' to represent 0 through 7.  See the '%x' code in
      #packetScanf(u8 * packet, const char * format,...)  for a way to read in a value that was
      printed with code \c OCT, and in #facePrintf(u8 face, const char * format,...)  for an
      alternate way to print in this representation.
   - #BIN: Print one to 32 bytes containing the base-2 representation of the 32 bits of \a num,
      using the ASCII characters '0' and '1' to represent 0 and 1.  See the '%b' code in
      #packetScanf(u8 * packet, const char * format,...)  for a way to read in a value that was
      printed with code \c BIN, and in #facePrintf(u8 face, const char * format,...)  for an
      alternate way to print in this representation.
   - #B36: Print one to six bytes containing the base-36 representation of the 32 bits of \a num,
      using the ASCII characters '0' through '9' to represent 0 through 9, and 'A' through 'Z' to
      represent 10 through 35.  See the '%t' code in #packetScanf(u8 * packet, const char *
      format,...)  for a way to read in a value that was printed with code \c B36, and in
      #facePrintf(u8 face, const char * format,...)  for an alternate way to print in this
      representation.
   - #BELONG: Print exactly four bytes containing the 32 bits of \a num, with the four bytes in 'big
      endian order', also known as 'network order'.  See the '%l' code in #packetScanf(u8 * packet,
      const char * format,...) for a way to read in a value that was printed with code \c BELONG,
      and in #facePrintf(u8 face, const char * format,...)  for an alternate way to print in this
      representation.  ]
      
      \c BELONG is called a 'binary' code, but it must not be confused with #BIN.  With \c BELONG,
      each of the four output bytes may each contain any possible bit pattern, whereas with code
      #BIN each output byte is either the ASCII code for '0' or the ASCII code for '1'.
   - #BESHORT: Print exactly two bytes containing the low-order 16 bits of \a num, with the two
      bytes in 'big endian order', also known as 'network order'.  See the '%h' code in
      #packetScanf(u8 * packet, const char * format,...) for a way to read in a value that was
      printed with code \c BESHORT, and in #facePrintf(u8 face, const char * format,...)  for an
      alternate way to print in this representation.

      \c BESHORT is called a 'binary' code, but it must not be confused with #BIN.  With \c BESHORT,
      each of the two output bytes may each contain any possible bit pattern, whereas with code #BIN
      each output byte is either the ASCII code for '0' or the ASCII code for '1'.
   - #BYTE: Print exactly one byte containing the low-order 8 bits of \a num.  See the '%c' code in
      #packetScanf(u8 * packet, const char * format,...) for a way to read in a value that was
      printed with code \c BYTE, and in #facePrintf(u8 face, const char * format,...)  for an
      alternate way to print in this representation.

      \c BYTE is called a 'binary' code, but it must not be confused with #BIN.  With \c BYTE, the
      output byte may each contain any possible bit pattern, whereas with code #BIN each output byte
      is either the ASCII code for '0' or the ASCII code for '1'.

   In addition to those codes, it is also possible to print in other bases between 2 and 36, if
   there is call to do so.  For example, using a \a code value of 13 will select printing in base
   13, using the ASCII characters '0' through '9' and 'A' through 'C'.

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param num A 32 bit number to be printed in a particular format.
 
  \param code What format to use (see above).
 
  \blinks E_API_FORMAT_ARG If \a code is an illegal code.  The legal codes are #BELONG, #BESHORT or
  #BYTE, or any value from 2 to 36 (which includes #DEC, #HEX, #OCT, #BIN, and #B36).
 
  \sideeffect The currently xmit packet of \a face increases in length as needed by the selected
              format, if packet length and buffer space room is available.

  Sample all-ASCII printable packet generation:
  \usage
  \code
    void myHandler(u8 *) {
      facePrint(SOUTH,"L 1234 in:");
      facePrint(SOUTH," base 10="); facePrint(SOUTH, 1234, DEC); // Prints 1234
      facePrint(SOUTH," base 16="); facePrint(SOUTH, 1234, HEX); // Prints 4D2
      facePrint(SOUTH," base 8=");  facePrint(SOUTH, 1234, OCT); // Prints 2322
      facePrint(SOUTH," base 2=");  facePrint(SOUTH, 1234, BIN); // Prints 10011010010
      facePrint(SOUTH," base 36="); facePrint(SOUTH, 1234, B36); // Prints YA
      facePrintln(SOUTH);
    }
  \endcode

  Sample binary packet generation:
  \usage
  \code
    void setup() { /\* nothing to do *\/ }
    void loop() {                                  // Print a packet containing exactly eight bytes: 
      facePrint(ALL_FACES,"t");                    //  1 byte containing an ASCII 't'
      facePrint(ALL_FACES,millis(),BELONG);        // +4 bytes of board uptime in big endian
      facePrint(ALL_FACES,random(10000),BESHORT);  // +2 bytes of a random number 0..9999 in big endian
      facePrintln();                               // +1 byte containing an ASCII newline '\n'
      delay(1000);                                 // Wait a second, then do it again.
    }
  \endcode
 */
void facePrint(u8 face, unsigned long num, int code) {
  facePrint(face,num,code,false,0,false);
}

void facePrint(u8 face, unsigned long num, int code, bool printneg, int width, bool zerofill) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->print) return;

  switch (code) {
  case BELONG:  
    facePutcImpl(face,(num>>24)&0xff); 
    facePutcImpl(face,(num>>16)&0xff);
    /* FALL THROUGH */

  case BESHORT: 
    facePutcImpl(face,(num>>8)&0xff);
    /* FALL THROUGH */

  case BYTE: 
    facePutcImpl(face,(num>>0)&0xff); 
    break;

  default:
    API_ASSERT(code >= 2 && code <= 36, E_API_FORMAT_ARG);
    facePrintInBase(face, num, code, printneg, width, zerofill);
  }
}

/**
   The same as #facePrint(u8 face, unsigned long num, int code), \e except that if \a num is
   negative \e and \a code is \c DEC, then prints \a num as a negative, with a leading '-'.

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param num A 32 bit number to be printed in a particular format.
 
  \param code What format to use (see #facePrint(u8 face, unsigned long num, int code)).

  \blinks E_API_FORMAT_ARG If \a code is an illegal code.  The legal codes are #BELONG, #BESHORT or
  #BYTE, or any value from 2 to 36 (which includes #DEC, #HEX, #OCT, #BIN, and #B36).
 
  \sideeffect The currently xmit packet of \a face increases in length as needed by the selected
              format, if packet length and buffer space room is available.

  \usage
  \code
    ...
      int signedNum = -1;
      u32 unsignedNum = signedNum;
      facePrint(WEST,signedNum,DEC);               // Prints "-1": Signed num with code DEC
      facePrint(WEST,unsignedNum,DEC);             // Prints "4294967295": Unsigned num with code DEC
      facePrint(WEST,signedNum,HEX);               // Prints "FFFFFFFF": Non-DEC codes are always
      facePrint(WEST,unsignedNum,HEX);             // Prints "FFFFFFFF": printed as unsigned
    ...
  \endcode
 */
void facePrint(u8 face, long num, int code) {
  facePrint(face,(unsigned long) num,code,true,-1,false);
}

// printFloat is based on the version by dmellis from the Arduino distribution (LGPL also)
static void facePrintFloat(u8 face, double number, u8 digits) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->print) return;

  // Handle negative numbers
  if (number < 0.0) {
    facePutcImpl(face,'-');
    number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (u8 i=0; i<digits; ++i)
    rounding /= 10.0;

  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  facePrint(face,int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    facePutcImpl(face,'.');

  // Extract digits from the remainder one at a time
  while (digits-- > 0) {
    remainder *= 10.0;
    int toPrint = int(remainder);
    facePutcImpl(face,toPrint+'0');
    remainder -= toPrint;
  }
}

/**
   Print \a val in a floating-point ASCII representation, with two digits to the right of the right
   of the decimal point.

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \param val A double floating point value to be printed.
 
  \sideeffect The currently xmit packet of \a face increases in length as needed to print \a val, if
  packet length and buffer space room is available.

  \usage
  \code
    ...
      float fnum = 1.23;
      double dnum = 355/113.0;
      facePrint(WEST,fnum);      // Prints "1.23" (float expands to double)
      facePrint(WEST,dnum);      // Prints "3.14" (355/113 is an ancient approximation of pi!)
    ...
  \endcode
 */
void facePrint(u8 face, double val) {
  facePrintFloat(face,val,2);
}

/**
   Print the 'face code' of \a face to the current xmit packet of \a face.  See also the '%F' format of 
   #facePrintf(u8 face, const char * format,...) for another way to perform this action, and in 
   #packetScanf(u8 * packet, const char * format,...) for a way to read in a value that was printed with
   this method.

  \param face A face code indicating which face(s) to print to.  If \a face is #NORTH, #SOUTH,
  #EAST, or #WEST, then 'N', 'S', 'E', or 'W' is printed, respectively.  If \a face is #ALL_FACES,
  then for each eligible basic face, \e that \e face's \e own face code is printed to the
  corresponding face.  If it is some other 'special' or 'virtual' face, then what is printed depends
  on the specific code involved.
 
  Not so amazing sample:
  \usage
  \code
    ...
      facePrintFace(NORTH);          // Prints 'N' to the NorthFace
      facePrintFace(WEST);           // Prints 'W' to the WestFace
    ...
  \endcode

  Slightly more interesting sample:
  \usage
  \code
    ...
      facePrint(ALL_FACES,"L You are my "); // These three lines print "L You are my N" to the NorthFace,
      facePrintFace(ALL_FACES);             // "L You are my S" to the SouthFace, "L You are my E" to the
      facePrintln(ALL_FACES);               // EastFace, and "L You are my W" to the WestFace (assuming
    ...                                     // they are all connected and processing packets).
  \endcode

 */
void facePrintFace(u8 face) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->printFaceCode) return;
  fd->printFaceCode(face);
}

/**
   Terminate the current xmit packet on \a face.  Normally the effect of this is to print an ASCII
   newline character, '\n', but that behavior may be altered by the packet framing in use on the \a
   face.
 */

void facePrintln(u8 face) {
  const FacePrinter * fd = getHandler(face);
  if (!fd || !fd->println) return;
  facePrintlnImpl(face);
}
void facePrintln(u8 face, u8 byte) {
  facePrint(face,byte);
  facePrintln(face);
}
void facePrintln(u8 face,const char * str) {
  facePrint(face,str);
  facePrintln(face);
}
void facePrintln(u8 face,const u8 * str, u32 len) {
  facePrint(face,str,len);
  facePrintln(face);
}
void facePrintln(u8 face,int decimal) {
  facePrint(face,decimal);
  facePrintln(face);
}
void facePrintln(u8 face,unsigned int decimal) {
  facePrint(face,decimal);
  facePrintln(face);
}
void facePrintln(u8 face,long decimal) {
  facePrint(face,decimal);
  facePrintln(face);
}
void facePrintln(u8 face,unsigned long decimal) {
  facePrint(face,decimal);
  facePrintln(face);
}
void facePrintln(u8 face,unsigned long decimal, int code) {
  facePrint(face,decimal,code);
  facePrintln(face);
}
void facePrintln(u8 face,double val) {
  facePrint(face,val);
  facePrintln(face);
}
/**
   Prints a 'packet check byte' and then terminates the packet via #facePrintln(u8 face).  A \e
   packet \e check \e byte is a single byte value that can optionally be placed as the last byte of
   a packet, to provide one measure of 'integrity check' that the packet data was not corrupted
   during transmission.  The value of a packet check byte depends on on \e all \e the \e previous \e
   bytes in the packet, so if any bytes of the packet were garbled while in transit, it is fairly
   likely, though \e not \e guaranteed, that the check byte will fail to match so the error can be
   detected.

   See the '%\n' format of #facePrintf(u8 face, const char * format,...) for another way to append a
   check byte to a packet, and see #packetReadCheckByte(u8 * packet) or the '%\n' format of
   #packetScanf(u8 * packet, const char * format,...) for ways to read and verify a check byte that
   was appended to a packet.

  \param face A face code indicating which face(s) to print to.  May be a 'basic face' like #NORTH
              or #WEST, a 'special face' like #ALL_FACES, or even a 'virtual face' defined by the
              sketch (see #faceSetPrinter()).
 
  \sideeffect The currently xmit packet of \a face increases in length by one byte containing the
              computed check byte, if packet length and buffer space room is available.

 */
void facePrintlnCheckByte(u8 face) {
  API_ASSERT_MAX(face,MAX_FACE_INDEX);
  facePrintln(face,faceCheckBytes[face],BYTE);
}

static void facePrintNoFacesInternal(u8, u8) { }
static void facePrintlnNoFacesInternal(u8) { }
static void facePrintFaceCodeNoFacesInternal(u8) { }
static const char * faceGetFaceNameNoFacesInternal(u8) { return "NO_FACES"; }
static const FacePrinter noFacesInternalPrinter = {
  facePrintNoFacesInternal,
  facePrintlnNoFacesInternal,
  facePrintFaceCodeNoFacesInternal,
  faceGetFaceNameNoFacesInternal
};

void print_startup_initialization() {
  faceSetPrinter(NO_FACES,&noFacesInternalPrinter);
}

