/*                                       -*- mode:C++; fill-column: 100 -*-
  SFBSerial.h - 'Platform-independent' serial port support
  Copyright (C) 2008 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 SFBSerial.h Support for high-level packet exchange over serial lines.
  \author David H. Ackley.  
  \date (C) 2008 All rights reserved.
  \lgpl
 */
#ifndef SFBSERIAL_H
#define SFBSERIAL_H

#include "SFBConstants.h"       /* For FACE_COUNT */
#include "SFBAlarm.h"           /* For SFBAlarmIndexType */ 
#include "SFBReactor.h"         /* For PacketHandler, DispatchHandler, Body */ 
#include "SFBHWSerial.h"        /* For hardware-specific serial support */ 
#include "SFBPrint.h"           /* For facePrint* */ 

/**
 * Single digit numeric codes for the "standard baud rates" for interSFB communications.  Although
 * the SFB serial hardware and software is capable of communicating at a wide range of standard and
 * non-standard baud rates (see begin(int,bool)), only the rates described here are used in the "SB"
 * and "SS" packets sent between SFB's during the "baud rate negotiation" (BRN) protocol, and thus
 * only one of these rates can be selected by that process.
 */
enum StandardBaudRateCodes {
  SFBAUD300 = 0,                ///< 300 baud.
  SFBAUD1200 = 1,               ///< 1200 baud.
  SFBAUD9600 = 2,               ///< 9600 baud; the "rendezvous rate" (see #SFBAUD_RENDEZVOUS).
  SFBAUD57600 = 3,              ///< 57600 baud.
  SFBAUD115200 = 4,             ///< 115200 baud; a common rate for communications between SFBs.
  SFBAUD230400 = 5,             ///< 230400 baud; often achievable by SFBs (depending on interrupt load).
  SFBAUD500000 = 6,             ///< 500000 baud; may be achievable by SFBs, but usually not reliable.
  SFBAUD1000000 = 7,            ///< 1000000 baud; future use; unachievable by existing SFBs.
  SFBAUD2000000 = 8,            ///< 2000000 baud; future use; unachievable by existing SFBs.
  SFBAUD3000000 = 9,            ///< 3000000 baud; future use; unachievable by existing SFBs.

  SFBAUD_RENDEZVOUS = 2         ///< Code 2 (#SFBAUD9600) is the "rendezvous speed" used for initial
                                ///  interSFB communications.  The baud rade indicated by this code
                                ///  must be supported by every "SFB-communications-aware"
                                ///  device. (On the other hand, it is the _only_ baud rate that is
                                ///  so required; support for any of the other baud rate codes is
                                ///  optional.)
};

/**
 * A serial communications "endpoint", implementing the SFB packet protocol, including baud rate
 * negotiation (BRN).  The SFBSerial class delegates to hardware-specific #SFBHWSerial objects to
 * perform actual input and output; the class itself is responsible for performing BRN and
 * presenting a generally uniform communications API.
 */
class SFBSerial {

public:

  /** \name Initialization */
  /*@{*/
  void begin() ;

  void begin(u32 baud, bool manual = true) ;

  void reserve() ;            
  /*@}*/

  /** \name Printing */  /* The endless, hideous print routines.. */
  /*@{*/

  /** Same as #facePrint(u8 face, const char * str) given this #getFace(). */
  void print(const char * str) {         facePrint(face,str); } 

  /** Same as #facePrint(u8 face, const u8 * str, u32 len) given this #getFace(). */
  void print(const u8 * str, u32 len) {  facePrint(face,str,len); } 

  /** Same as #facePrint(u8 face, int decimal) given this #getFace(). */
  void print(int decimal) {              facePrint(face,decimal); }

  /** Same as #facePrint(u8 face, unsigned int decimal) given this #getFace(). */
  void print(unsigned int decimal) {     facePrint(face,decimal); }

  /** Same as #facePrint(u8 face, long decimal) given this #getFace(). */
  void print(long decimal) {             facePrint(face,decimal); }

  /** Same as #facePrint(u8 face, unsigned long decimal) given this #getFace(). */
  void print(unsigned long decimal) {    facePrint(face,decimal); }

  /** Same as #facePrint(u8 face, unsigned long decimal, int code) given this #getFace(). */
  void print(long decimal, int code) {   facePrint(face,decimal,code); }

  /** Same as #facePrint(u8 face, double val) given this #getFace(). */
  void print(double val) {               facePrint(face,val); }

  /** Same as #facePrintln(u8 face) given this #getFace(). */
  void println() {                       facePrintln(face); }

  /** Same as #facePrintln(u8 face, const char * str) given this #getFace(). */
  void println(const char * str) {       facePrintln(face,str); }

  /** Same as #facePrintln(u8 face, const u8 * str, u32 len) given this #getFace(). */
  void println(const u8 * str, u32 len) {facePrintln(face,str,len); }

  /** Same as #facePrintln(u8 face, int decimal) given this #getFace(). */
  void println(int decimal) {            facePrintln(face,decimal); }

  /** Same as #facePrintln(u8 face, unsigned int decimal) given this #getFace(). */
  void println(unsigned int decimal) {   facePrintln(face,decimal); }

  /** Same as #facePrintln(u8 face, long decimal) given this #getFace(). */
  void println(long decimal) {           facePrintln(face,decimal); }

  /** Same as #facePrintln(u8 face, unsigned long decimal) given this #getFace(). */
  void println(unsigned long decimal) {  facePrintln(face,decimal); }

  /** Same as #facePrintln(u8 face, long decimal) given this #getFace(). */
  void println(long decimal, int code) { facePrintln(face,decimal,code); }

  /** Same as #facePrintln(u8 face, long val) given this #getFace(). */
  void println(double val) {             facePrintln(face,val); }

  /** Same as #facePrintlnCheckByte(u8 face) given this #getFace(). */
  void printlnCheckByte() ;
  /*@}*/

  /** \name Accessors */
  /*@{*/
  bool isInUse() ;  ///< Are we in use?  \return true if in use as either serial port or GPIOs.

  bool isManualMode() ; ///< Are we in manual mode? \return true if we're running as a serial port in byte-at-a-time mode.

  bool isReflexMode() ; ///< Are we in reflex mode? \return true if we're running as a serial port in reflex packet handling mode.

  bool isConsciousMode() ; ///< Are we in conscious mode? \return true if we're running as a serial port in conscious packet handling mode.

  void switchToBlocking() ;
  
  u32 getBaud() { return baud; } ///< What is our baud rate?  \return our current baud rate.

  bool getOddParity() { return flags&FLAG_ODD_PARITY; } ///< Are we running odd parity (vs none)?  \return true if odd parity, false is none.

  u32 getFace() { return face; }  ///< What is our face number?  \return our face code.

  /** Is this SFBSerial currently capable of printing?

      \returns \c true if one of the following is true:

               - this SFBSerial was started under conscious control (using SFBSerial::begin(u32
                 baud)), or

               - this SFBSerial was started under manual control (using SFBSerial::begin(u32 baud,
                 bool manual) with \c manual omitted or set to \c true), or

               - this SFBSerial was started under reflex control (using SFBSerial::begin()) and a
                 successfully negotiated baud rate is currently in use.
     
               and returns false otherwise.
  */
  bool ready() ;

  u32 inputPackets() ;
  u32 outputPackets() ;

  /** Access the underlying #SFBHWSerial object that actually performs the input and output for this
      SFBSerial. 
      
      \returns a reference to the underlying #SFBHWSerial.
  */
  SFBHWSerial & getHWSerial() { return hwSerial; }

  /** Is this SFBSerial operating and in full reflex control?

      \returns \c true if this SFBSerial was started via a call to SFBSerial::begin() since the most
      recent call to SFBSerial::begin(u32 baud), SFBSerial::begin(u32 baud, bool manual), or
      SFBSerial::reserve(), and returns \c false otherwise. */
  bool backgroundProcessingActive() ;

  /*@}*/

  /** \name Legacy byte-at-a-time */
  /*@{*/

  /**
   * Check if at least one byte of input is available.  Only callable after the serial
   * face has been started in 'manual' mode (i.e., via 'begin(BAUD_RATE)' or 'begin(BAUD_RATE,true)').
   *
   * @return \c true if and only if a byte can now be #read() from this face, \c false otherwise. 
   *
   * @blinks #E_API_ILLEGAL_STATE if the face has not been opened (most recently) in 'manual' mode.
   */
  bool available() ;

  /**
   * Read the next byte from this serial face, if any are immediately available.  Only callable
   * after the serial face has been started in 'manual' mode (i.e., via 'begin(BAUD_RATE)' or
   * 'begin(BAUD_RATE,true)').
   *
   * @return the next byte read, greater than or equal to 0, or -1 if
   * no byte can immediately be read
   *
   * @blinks #E_API_ILLEGAL_STATE if the face has not been opened (most recently) in 'manual' mode.
   */
  int read();

  /**
   * Peek at the next byte from this serial face, if any are immediately available, without
   * consuming it.  Only callable after the serial face has been started in 'manual' mode (i.e., via
   * 'begin(BAUD_RATE)' or 'begin(BAUD_RATE,true)').  Any byte returned by this method is \e not
   * removed from the input stream, and the same byte will be returned on the next call to via
   * #read.
   *
   * @return the next byte to be read, greater than or equal to 0, or -1 if no byte is immediately
   * available to be read
   *
   * @blinks #E_API_ILLEGAL_STATE if the face has not been opened (most recently) in 'manual' mode.
   *
   * @since 0.9.3
   */
  int peek();

  /*@}*/

  /** \name Conscious packet handling */
  /*@{*/

  /**
   * Read the next packet from this serial face, if any are immediately available.  Only callable
   * after the serial face has been started in 'conscious' mode (i.e., via
   * 'begin(BAUD_RATE,false)').  Returns a null pointer if no complete packet is available.  When a
   * non-null pointer \e is returned, it points to memory owned by the core libraries, not the
   * sketch, and the returned pointer is valid only ubtil the \e next call to #readPacket on the
   * same face (or the next call on #begin, #reserve, or #flush on the face, whichever comes first.)
   *
   * @return a pointer to the next packet read, or 0 if no complete packet is currently available.  
   *
   * @blinks #E_API_ILLEGAL_STATE if the face has not been opened (most recently) in 'conscious'
   * mode.
   *
   * @sideeffect On a non-null return, the input corresponding to the returned packet has been
   * removed from the serial face input stream.
   *
   * @since 0.9.3
   */
  u8 * readPacket();

  /*@}*/

  /** \name Configuration */
  /*@{*/

  void setPreferredBaudCode(u8 code, bool renegotiateNow = false) ;

  bool dispatch(DispatchHandler dispatcher = 0, u32 maxPackets = 1) ;

  void suppress(const char type) ;
  void allow(const char type) ;

  void reflex(const char type, PacketHandler pfunc) ;

  void otherwise(PacketHandler pfunc) ;

  /*@}*/

  /** \name Low-level and internal use */
  /*@{*/

  /** Low level routine to print a single byte, if possible.  Intended for library internal use
      only.  Designed for use when the underlying #SFBHWSerial is being run via interrupts (in other
      words, in 'reflex' or 'conscious', but not 'manual' modes).

      \param byte the byte to print

      \sideeffect If #ready() is \c true, a byte is printed to the underlying #SFBHWSerial, with
                  whatever side effects that implies.
  */
  void putcInterrupt(u8 byte) ;

  /** Low level routine to terminate a packet, if possible.  Intended for library internal use only.
      Designed for use when the underlying #SFBHWSerial is being run via interrupts (in other words,
      in 'reflex' or 'conscious', but not 'manual' modes).

      \param byte the byte to print

      \sideeffect If #ready() is \c true, the current packet in the underlying #SFBHWSerial is
                  terminated, with whatever side effects that implies.  
  */
  void terpriInterrupt() ;

  /** Low level routine to print a single byte.  Intended for library internal use only.  Designed
      for use when the underlying #SFBHWSerial is being run via blocking calls (in other words,
      'manual' mode only).

      \param byte the byte to print

      \sideeffect A byte is output to the hardware, which may or may not have any externally visible
                  effects, depending on hardware configuration.
  */
  void putcBlocking(u8 byte) ;

  /** Low level routine to terminate a packet.  Intended for library internal use only.  Designed
      for use when the underlying #SFBHWSerial is being run via blocking calls (in other words,
      'manual' mode only).

      \sideeffect A single newline byte (ASCII '\n', value 10, \c 0xa) is output to the hardware,
                  which may or may not have any externally visible effects, depending on hardware
                  configuration.
  */
  void terpriBlocking() ;

  void write(u8 byte) { lowPutc(byte); }
  void write(const char * str) { print(str); }
  void write(u8 * buf, u32 len) { print(buf,len); }

  /**
   * Flushes the face of incoming serial data.  That is, any call to #read() or #available() will
   * return only data received after all the most recent call to flush().  Note this call is legal
   * whether the serial face was started in 'manual' mode (i.e., via 'begin(BAUD_RATE)' or
   * 'begin(BAUD_RATE,true)'), or 'conscious' mode (via 'begin(BAUD_RATE,false)'), or full packet
   * processing mode (via 'begin()') -- in the latter two cases all pending packets are discarded.
   */
  void flush() ;

  SFBSerial(int face) ; ///< Construct an SFBSerial with face number \a face.  Intended for library
                        ///  internal use during system startup only.

  /** Low level routine to initialize the #SFBSerial subsystem.  Intended for library internal use
      during system startup only.

      \sideeffect The #SFBSerial subsystem is initialized: Reflexes are installed for several packet
                  types, #SFBAlarm's are created to manage timeouts during communications, etc.
  */
  static void startup_initialization() ;

  /*@}*/

  //XXX
private:

  /* Low-level internal routines so BRN routines can talk before BRN is complete.. */
  void lowPrint(const char * str) ;
  void lowPrintln(const char * str) ;
  void lowPutc(const u8 byte) ;
  void lowTerpri() ;
  void lowPrintOurBRO() ;        /* Print our current baud rate offer */ 

  void maybeBlockForOutput() ;  /* Block until output space available, if we're FLAG_BLOCKING */ 

  static PacketBuffer sharedBuffer;

  SFBHWSerial hwSerial;        /* Platform-specific state and methods */

  u32 baud;
  u32 nextTimeout;             /* when we're next to act */ 
  SFBAlarmIndexType alarmNumber; /* Our alarm number, if != 0 */ 
  const u8 face;                 /**< The face number for this SFBSerial */ 
  u8 flags;
  u8 state;                    /* Current state in BRN state machine */ 
  u8 preferredCode;            /* Our current preferred baud rate code */ 
  u8 selectedCode;             /* negotiated baud rate code */ 
  bool selectedOdd;            /* negotiated odd parity (else none) */ 
  bool haveBufferedByte;       /* for ::available() */ 
  u8 bufferedByte;
  enum {
    STATE_RESET,                /* 0 Entered to send a reset packet */ 
    STATE_INIT,                 /* 1 Initial state */ 
    STATE_LISTEN,               /* 2 Sending/waiting for SIM */ 
    STATE_OFFER,                /* 3 Sent/waiting for SB */ 
    STATE_CONFIRM,              /* 4 Sent/waiting for SS */ 
    STATE_WAIT,                 /* 5 Delay after baud changed */ 
    STATE_DONE,                 /* 6 BRN finished */ 
    STATE_SHOWTIME,             /* 7 Queueing and dispatching packets */ 
    STATE_CHECKIN,              /* 8 SRU sent to request SIM */ 
    MAX_STATES
  };

  enum {
    FLAG_RUNNING =   0x01,     /* Port is in use (on our side.  This doesn't imply a live connection.) */
    FLAG_ODD_PARITY= 0x02,     /* Port is using odd parity (vs no parity) */
    FLAG_CONSCIOUS = 0x04,     /* Port is under conscious control - no BRN or bkgd packets will be sent */
    FLAG_BRN_DONE  = 0x08,     /* Baud rate negotiation has succeeded or is under conscious control */
    FLAG_BLOCKING_BYTES= 0x10, /* Port is running in byte-by-byte mode, with blocking output */
    FLAG_GPIOS     = 0x20,     /* Port pins are in use as non-UART pins, we can't touch them */
    FLAG_CONNECTED = 0x40,     /* We have received data lately on this face; there's something out there */
    FLAG_RSRVD8    = 0x80      /* Reserved */
  };

  void start(u32 baud, u8 startFlags) ;

  void stop() ;

  void setBaudRate(u32 baud, bool isOddParity = false) ;

  void setOddParity(bool isOdd) {
    if (isOdd) flags |= FLAG_ODD_PARITY;
    else flags &= ~FLAG_ODD_PARITY;
  }

  typedef void (*InitHandler)(u8 face);
  static const char backgroundProcessorTypes[];
  static const PacketHandler backgroundProcessorHandlers[];
  static const InitHandler backgroundProcessorInits[];

  void backgroundProcessingInit() ;

  static void stateTimeout(u32 when, void * arg) ;

  void step(u32 when, u8 * spacket) ; /* Step the state machine.  spacket==0 means timeout occurred */

  bool stepInit(u32 when, u8 * spacket) ; /* step in state INIT */
  bool stepListen(u32 when, u8 * spacket) ; /* step in state LISTEN */
  bool stepOffer(u32 when, u8 * spacket) ; /* step in state OFFER */
  bool stepConfirm(u32 when, u8 * spacket) ; /* step in state CONFIRM */
  bool stepWait(u32 when, u8 * spacket) ; /* step in state WAIT */
  bool stepDone(u32 when, u8 * spacket) ; /* step in state DONE */
  bool stepShowtime(u32 when, u8 * spacket) ; /* step in state SHOWTIME */
  bool stepCheckin(u32 when, u8 * spacket) ; /* step in state CHECKIN */

  void setState(u8 newState, u32 aboutHowManyMs) ;

  static void ppacketDispatcher(u8 * packet) ;

  static void rpacketDispatcher(u8 * packet) ;

  static void spacketDispatcher(u8 * packet) ;
  static void spacketInit(u8 face) ;

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

/* Some utility functions for packet handling */
extern bool zPacketPrefix(u8 * p1, const char * to) ;
extern bool packetPrefix(u8 * p1, const u8 * to, const u32 len) ;

extern SFBSerial Faces[FACE_COUNT]; // Declared here but defined in SFBHWSerial(Board|Host)
#define NorthFace Faces[NORTH]
#define SouthFace Faces[SOUTH]
#define EastFace Faces[EAST]
#define WestFace Faces[WEST]

/* Make just plain 'Serial' be the same as 'NorthSerial',
 * so that Arduinoish code will have some place to go.
 *
 * Ditto for 'Serial1', etc, so Arduino Mega examples can fly
 */
#define Serial NorthFace
#define Serial1 SouthFace
#define Serial2 EastFace
#define Serial3 WestFace

#endif  /* SFBSERIAL_H */

