/*                                              -*- mode:C++; fill-column:100 -*-
  SFBPrint.h - Generalized print formatting routines
  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$
*/

/**
  \file SFBPrint.h Support for basic formatted printing
  \author David H. Ackley.  
  \date (C) 2009 All rights reserved.
  \lgpl
 */
#ifndef SFBPRINT_H
#define SFBPRINT_H

#include "SFBConstants.h"       /* For FACE_COUNT */
#include "SFBAlarm.h"           /* For SFBAlarmIndexType */ 
#include "SFBReactor.h"         /* For PacketHandler, Body */ 
#include "SFBFrame.h"

/* The 'plain function' print methods dish to all reflex-controlled faces! */

extern void print(const char * str);            ///< \eq #facePrint(u8 face, const char * str)
                                                ///  with \c face set to #ALL_FACES.
extern void print(const u8 * str, u32 len);     ///< \eq #facePrint(u8 face, const u8 * str,u32 len) 
                                                ///  with \c face set to #ALL_FACES.
extern void print(int decimal);                 ///< \eq #facePrint(u8 face, int decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void print(unsigned int decimal);        ///< \eq #facePrint(u8 face, unsigned long decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void print(long decimal);                ///< \eq #facePrint(u8 face, long decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void print(unsigned long decimal);       ///< \eq #facePrint(u8 face, unsigned long decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void print(long decimal, int code);      ///< \eq #facePrint(u8 face, unsigned long decimal, int code)
                                                ///  with \c face set to #ALL_FACES.
extern void print(double val);                  ///< \eq #facePrint(u8 face, double val) 
                                                ///  with \c face set to #ALL_FACES.
extern void printFace();                        ///< \eq #facePrintFace(u8 face) 
                                                ///  with \c face set to #ALL_FACES.

extern void println();                          ///< \eq #facePrintln(u8 face) 
                                                ///  with \c face set to #ALL_FACES.
extern void println(const char * str);          ///< \eq #facePrintln(u8 face, const char * str)
                                                ///  with \c face set to #ALL_FACES.
extern void println(const u8 * str, u32 len);   ///< \eq #facePrintln(u8 face, const u8 * str,u32 len) 
                                                ///  with \c face set to #ALL_FACES.
extern void println(int decimal);               ///< \eq #facePrintln(u8 face, int decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void println(unsigned int decimal);      ///< \eq #facePrintln(u8 face, unsigned long decimal)
                                                ///  with \c face set to #ALL_FACES.
extern void println(long decimal);              ///< \eq #facePrintln(u8 face, long decimal) 
                                                ///  with \c face set to #ALL_FACES.
extern void println(unsigned long decimal);     ///< \eq #facePrint(u8 face, unsigned long decimal)  
                                                ///  with \c face set to #ALL_FACES.
extern void println(long decimal, int code);    ///< \eq #facePrintln(u8 face, unsigned long decimal, int code) 
                                                ///  with \c face set to #ALL_FACES.
extern void println(double val);                ///< \eq #facePrint(u8 face, double val) 
                                                ///  with \c face set to #ALL_FACES.
extern void printlnCheckByte();                 ///< \eq #facePrintlnCheckByte() 
                                                ///  with \c face set to #ALL_FACES.

extern void facePrint(u8 face, const char * str);
extern void facePrint(u8 face, const u8 * str, u32 len);
extern void facePrint(u8 face, int decimal); 
extern void facePrint(u8 face, unsigned int decimal);
extern void facePrint(u8 face, long decimal);
extern void facePrint(u8 face, unsigned long decimal);
//extern void facePrint(u8 face, long decimal, int code);
extern void facePrint(u8 face, unsigned long decimal, int code);
extern void facePrint(u8 face, unsigned long decimal, int code, bool printneg, int width, bool zerofill);
extern void facePrint(u8 face, double val);
extern void facePrint(u8 face, double val);
extern void facePrintFace(u8 face);

extern void facePrintln(u8 face); 
extern void facePrintln(u8 face, const char * str);        ///< \eq #facePrint(u8 face, const char * str) 
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, const u8 * str, u32 len); ///< \eq #facePrint(u8 face, const u8 * str,u32 len)
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, int decimal);             ///< \eq #facePrint(u8 face, int decimal) 
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, unsigned int decimal);    ///< \eq #facePrint(u8 face, unsigned int decimal) 
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, long decimal);            ///< \eq #facePrint(u8 face, long decimal) 
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, unsigned long decimal);   ///< \eq #facePrint(u8 face, unsigned long decimal)
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, unsigned long decimal, int code);
                                                           ///< \eq #facePrint(u8 face, unsigned long decimal, int code) 
                                                           ///  followed by #facePrintln().
extern void facePrintln(u8 face, double val);              ///< \eq #facePrint(u8 face, double val) 
                                                           ///  followed by #facePrintln().
extern void facePrintlnCheckByte(u8 face);

/**
 * A FacePrinter is capable of performing low-level printing operations to some assigned (physical
 * or 'virtual') face.  It contains up to four function pointers describing how to:
 * -# Print a single byte to a(n incomplete) packet,
 * -# Terminate a (previously incomplete) packet,
 * -# Print its face code to a(n incomplete) packet, and
 * -# Provide a zero-terminated name describing this #FacePrinter.
 *
 * The latter two methods are optional and may be null pointers.  The first two methods are must
 * either both be non-null (the typical case) or both null (e.g., for a '/dev/null' face that
 * discards all output).
 */
struct FacePrinter {
  void (*print)(u8 face, u8 byte);  ///< Handler to print a single data byte, or 0 if undefined.
  void (*println)(u8 face);         ///< Handler to print a packet terminator, or 0 if undefined.
  void (*printFaceCode)(u8 face);   ///< Handler to print a single-byte representation of its face
                                    ///code, or 0 if undefined.
  const char * (*getFaceName)(u8 face); ///< Handler to return a zstring name of this FacePrinter,
                                        ///or 0 if undefined.
};

extern bool faceHasPrinter(u8 face);     ///< \c true if this \a face has a #FacePrinter. Equivalent
                                         ///to faceGetPrinter(face)!=0
/**
   Get the #FacePrinter associated with this \a face, if any.
  
   @return A pointer to the #FacePrinter associated with \a face, or 0 if there is none, or if \a
           face is out of range.
   @since 0.9.7
 */
extern const FacePrinter * faceGetPrinter(u8 face);

/**
   Find an unused 'virtual face number', if any are available.  Returns \c true and \e stores \e
   into the reference variable \a foundFace if an available virtual face number is found.

   Note that even when this method returns \c true, it does \e not actually make the found virtual
   face be 'in use'.  See #faceSetPrinter() for that.
  
   @return \c true if an unused virtual face number was found, and \a foundFace was set to its
           value; \c false if all virtual faces are already in use.
 */
extern bool faceFindFreeFace(u8 & foundFace);

/**
   Associate a given #FacePrinter \a handler with an unused 'virtual face number' \a face.  Inverted
   by #faceUnsetPrinter.
   
   @blinks #E_API_MAX_RANGE if \a face is not a legal (virtual) face number number.

   @blinks #E_API_NULL_POINTER if \a handler is null

   @blinks #E_API_ZERO if the virtual face number \a face is already in use

   @blinks #E_API_ARGUMENT if the #FacePrinter pointed-to by \a handler has one but not both of its
                           \c print and \c println methods specified.

 */
extern void faceSetPrinter(u8 face, const FacePrinter * handler) ;

/**
   Remove any #FacePrinter association from the 'virtual face number' \a face.  Inverts the effect
   of a #faceSetPrinter call; makes \a face available to be returned by #findFreeFace.  Has no
   effect if the given \a face does not currently have an associated #FacePrinter.
   
   @blinks #E_API_MAX_RANGE if \a face is not a legal (virtual) face number number.

 */
extern void faceUnsetPrinter(u8 face) ;

/**
 * Stores the information needed to use a virtual face to print a packet into a user-supplied
 * buffer.  See #makePacketPrinter for details.
 */
struct PacketPrinterHeader {
  FacePrinter fp;
  u8 maxLen;
  u8 usedLen;
  u8 sourceFace;                // WARNING! sourceFace must be the last field of the struct!
  // u8 packetBuffer[0]; /* Illegal ISO C++, but just so you know: Packet buffer starts here. */
};

/**
   The number of bytes needed to store a #PacketPrinterHeader plus other associated overhead.  (This
   is technically the minimum legal size for 'bufferLength' in a #makePacketPrinter call; but
   normally the passed bufferLength would be substantially larger -- up to
   #PACKET_PRINTER_PACKET_BUFFER_SIZE -- because with this minimum size only a zero length packet
   can be printed.
 */
#define PACKET_PRINTER_BUFFER_OVERHEAD_BYTES \
  (sizeof(PacketPrinterHeader)+sizeof(PacketHeader)+3+1) // (up to) 3 for alignment + 1 null terminator
/**
   The number of bytes that guarantees a user buffer will have room to print a #MAX_PACKET_LENGTH
   packet.  See #makePacketPrinter() for details.
 */
#define PACKET_PRINTER_PACKET_BUFFER_SIZE \
  (PACKET_PRINTER_BUFFER_OVERHEAD_BYTES+MAX_PACKET_LENGTH) // room for a full packet

/**
   Define a virtual face that prints a packet into a user-supplied RAM buffer.  The normal packet
   printing functions print directly to the output of a given face (or all faces).  But, sometimes
   it is desirable to build a packet in memory without immediately sending it off-board, and this
   function, while a bit complicated to use, makes that possible.

   Note that only one packet can be stored at a time.  After a packet has been terminated, it
   remains available until the first byte of another packet is printed, at which time the buffer is
   reset and a new packet construction begins.

   This 'packet printer' functionality is built on top of the #faceFindFreeFace and #faceSetPrinter
   mechanisms.  So if there is a need to destroy a packet printer created by #makePacketPrinter (for
   example to use the buffer space for something else), the virtual face number originally returned
   in \a assignedFace should be freed by called #faceUnsetPrinter().
   
   @param assignedFace The assigned virtual face number, stored into by this function if it returns
                       \c true (and unaltered otherwise.)

   @param buffer The start of the user buffer space to use.  Note that the resulting printed packet
                 will \e NOT be located at this address!  Its address is given by the return value
                 from this function (if non-null).

   @param bufferLength The size of the buffer space pointed-to by \a buffer.  Must be at least
                       #PACKET_PRINTER_BUFFER_OVERHEAD_BYTES plus the number of bytes in the largest 
                       packet the user intends to print; #PACKET_PRINTER_PACKET_BUFFER_SIZE will
                       allow a maximum length packet can be stored (and so any \a bufferLength value
                       larger than that will simply waste memory.)
                       
   @param sourceFace The source face to assign to the packets printed into the buffer.  Must be one
                     of #NORTH, #SOUTH, #EAST, #WEST, #SPINE, #WMEM, or #BRAIN, and determines where
                     the search for reflexes starts, if a packet printed into the buffer is passed
                     to SFBReactor::trigger().  Depending on the intended uses of the resulting
                     packet, it may be required that \a sourceFace be one of the 'physical' faces
                     (#NORTH, #SOUTH, #EAST, #WEST); so when in doubt pick one of those.

   @return 0 if no virtual faces are available.

   @blinks #E_API_NULL_POINTER if \a buffer is null, otherwise returns the pointer to the address
                               where (each) printed packet will be stored.

   @blinks #E_API_GREATER_EQUAL if \a bufferLength is less than #PACKET_PRINTER_BUFFER_OVERHEAD_BYTES

   @since 0.9.7
 */
extern u8 * makePacketPrinter(u8 & assignedFace, u8 * buffer, u32 bufferLength, u8 sourceFace) ;

/* The one-time serial startup code */
extern void print_startup_initialization() ;

#endif  /* SFBPRINT_H */

