class tXBee
  { private:
      char ArgsString[50];
       int CharsReceived;
      bool InPiece;

      void ClearInputQueue();
      void DisplayLine(const char Header[]);
      void DisplayLine1(const char Header[]);
      void InstructRelay();
      void ProcessEndOfLine();
    public:
      bool LineWaiting;
      int  NPieces;       // includes incomplete pieces
      int  Piece[20];
      bool VC0706IsOK;

      void GetCharsFromBase();
      void NoLineWaiting();
      void setup();
      void TransmitToBase(const char BoardTag,
                          const tBoardSlot BoardSlot,
                          const char Payload[],
                          const unsigned long Checksum);
  }; // tXBee

 void tXBee::ClearInputQueue()
   { while (SerialXBee.available())
       { SerialXBee.read(); }
   } // tXBee::ClearInputQueue()

void tXBee::DisplayLine(const char Header[])
  { DS1307.DisplayHHMM("XBee ");
    Display.PrintString(Header);
    Display.PrintString(ArgsString);
  } // tXBee::DisplayLine

void DisplayLine1(const char Header[])
  { // just one parameter
    sprintf(ArgsString, " %d", Piece[1]);
    DisplayLine(Header);
  } // DisplayLine1

void tXBee::GetCharsFromBase()
   { //   called from main loop when at least one character is available and
     // no line is waiting
     
     char C;
 
     while (SerialXBee.available() and not LineWaiting)
       { // get at least one character
         C = SerialXBee.read();
         // TV.SmallPrint(C);
         // TV.SmallPrint('@');
         switch (C)
           { case    '\n':
             case EXCLAIM: ProcessEndOfLine();
                           break;
 
             case   BLANK:
             case     TAB: // TV.SmallPrint('>');
                           InPiece = false;
                           break;

                  default: if ((C >= '0') and (C <= '9'))
                             { // extend piece
                               if (not InPiece)
                                 { Piece[NPieces] = 0;
                                   NPieces++;
                                   InPiece = true;
                                 }
                               // TV.SmallPrint(C);
                               Piece[NPieces-1] = 10 * Piece[NPieces-1] + C - '0';
                               // Serial.printf("just set Piece[%d] = %d\\n", NPieces-1, Piece[NPieces-1]);
                             } // extend piece
                           break;
           } // switch
       } // get at least one character
   } // tXBee::GetCharsFromBase()

void tXBee::InstructRelay()
  { // debulk ProcessEndOfLine
    //        P0                P1            P2            note
    //    wtbRelayMode                                    always first
    // 11 wtbRelaySchedule    <msmStart>    <msmStop>
    // 12 wtbRelayTemps       <colder>       <hotter>
    //  6 wtbRelayHysteresis  (10 * Hys)
    //  7 wtbRelayLight        <darker>      <lighter>
    //  9 wtbRelayOff
    // 10 wtbRelayOn
    //  5 wtbRelayAction                                 always last

    float Hysteresis;
//     char Message[100];
    int P0 = Piece[0];
    int P1 = Piece[1];
    int P2 = Piece[2];
//
//     // sprintf(Message, "XBee.IR %d %d %d", P0, P1, P2);
//     // Infrastructure.SayInIDE(Message);
//
//     // sprintf(Message, "XBee: relays named %s and %s",
//     //                  Relays[0]->RelayName, Relays[1]->RelayName);
//     // Infrastructure.SayInIDE(Message);

    SetCrumb(crumbXBee, 99);
    switch (P0)
      { case     wtbRelayAction: // must be last
                                 DisplayLine1("action");
                                 RelayInfo.InstructionInProgress = false;
                                 Infrastructure.SayInIDE("  go\n");
                                 break;

        case wtbRelayHysteresis: DisplayLine1("hyster");
                                 Hysteresis = (float)P1 / 10.0;
                                 RelayInfo.ThermHysteresisF = Hysteresis;
                                 // sprintf(Message, "  set hysteresis to %.1f", Hysteresis);
                                 // Infrastructure.SayInIDE(Message);
                                 break;

        case      wtbRelayLight: DisplayLine("light");
                                 RelayInfo.LightLow = P1;
                                 RelayInfo.LightHigh = P2;
                                 // sprintf(Message, "  set light levels to %d-%d", P1, P2);
                                 // Infrastructure.SayInIDE(Message);
                                 break;

        case       wtbRelayMode: // must be first
                                 DisplayLine1("rly mode");
                                 RelayInfo.InstructionInProgress = true;
                                 RelayInfo.RelayMode = P1;
                                 // sprintf(Message, "Instructing %s", Relays[RelayPinIndex]->RelayName);
                                 // Infrastructure.SayInIDE(Message);
                                 // sprintf(Message, "  set RelayMode = %d", P1);
                                 // Infrastructure.SayInIDE(Message);
                                 break;

        case       wtbRelayOff: DisplayLine1("force off");
                                RelayInfo.RelayMode = rmForced;
                                RelayInfo.ForceOn = false;
                                // Infrastructure.SayInIDE("  force off");
                                break;

       case         wtbRelayOn: DisplayLine1("force on");
                                RelayInfo.RelayMode = rmForced;
                                RelayInfo.ForceOn = true;
                                // Infrastructure.SayInIDE("  force on");
                                break;

        case   wtbRelaySchedule: DisplayLine("schedule");
                                 RelayInfo.Sched_msmOnTime  = P1;
                                 RelayInfo.Sched_msmOffTime = P2;
                                 // sprintf(Message, "  set schedule to %d-%d", P1, P2);
                                 // Infrastructure.SayInIDE(Message);
                                 break;

        case      wtbRelayTemps: DisplayLine("temps");
                                 RelayInfo.TempLow  = (float)P1;
                                 RelayInfo.TempHigh = (float)P2;
                                 // sprintf(Message, "  set temps to %d-%d", P1, P2);
                                 // Infrastructure.SayInIDE(Message);
                                 break;
      } // switch
    NoLineWaiting();
    SetCrumb(crumbXBee, 158);
  } // tXBee::InstructRelay

void tXBee::NoLineWaiting()
   { InPiece = false;
     NPieces = 0;
     LineWaiting = false;
   } // tXBee::NoLineWaiting()

void tXBee::ProcessEndOfLine()
   { const int wtbRelayA = wtbRelayAction;
     const int wtbRelayZ = wtbRelayWhichTemp;

     SetCrumb(crumbXBee, 171);
     if (NPieces > 0)
       { // nondegenerate
         sprintf(ArgsString, " %d %d %d", Piece[1], Piece[2], Piece[3]);

         switch (Piece[0])
           {  case    wtbBroadcastLocalTime: //                              hour     minute
                                             sprintf(ArgsString, " %d %d", Piece[4], Piece[5]);
                                             DisplayLine("time");
                                             //                year      month     day       hour     minute   second
                                             DS1307.SetDS1307(Piece[1], Piece[2], Piece[3], Piece[4], Piece[5],  0);
                                             NoLineWaiting();
                                             break;

              case wtbBroadcastOutsideTempF: DisplayLine1("temp");
                                             Infrastructure.RelayTempF = (float)Piece[1] / 100.0;
                                             Infrastructure.RelayTempDefined = true;
                                             NoLineWaiting();
                                             break;

              case     wtbBroadcastPressure: DisplayLine1("pressure");
                                             Infrastructure.RecentPressureInchesHg = (float)Piece[1] / 100.0;
                                             Infrastructure.RecentPressureDefined = true;
                                             NoLineWaiting();
                                             break;

              case             wtbDebugging: DisplayLine1("debugging");
                                             if (Piece[1] == 0)
                                               { Infrastructure.Debugging = false; }
                                             else
                                               { Infrastructure.Debugging = true; }
                                             break;

             case            wtbDeleteJPEGs: DisplayLine1("del JPEGs");
                                             // FileSystem.DeleteJPEGs();     ***********************
                                             NoLineWaiting();
                                             break;

             case         wtbSpeedupChanged: DisplayLine1("speedup");
                                             // Serial.printf("XBee.ProcessEndOfLine.Piece[1] = %d\\n", Piece[1]);
                                             Infrastructure.Speedup = (Piece[1] > 0);
                                             NoLineWaiting();
                                             break;

             case        wtbTakeVC0706Image: DisplayLine1("snapshot");
                                             if (not VC0706IsOK)
                                               {  NoLineWaiting(); }
                                             break;

                                    default: if ((Piece[0] >= wtbRelayA) and (Piece[0] <= wtbRelayZ))
                                               { InstructRelay(); }
                                             break;
           } // switch
       } // nondegenerate
     LineWaiting = (NPieces > 0);
     SetCrumb(crumbXBee, 223);
   } //  tXBee::ProcessEndOfLine()

void tXBee::setup()                             // peripheral XBee
  { // relays declared but not set up
    SetCrumb(crumbXBee, 228);
    CharsReceived = 0;
    VC0706IsOK    = false;
    // Relays[0]     = &Relay_1;
    // Relays[1]     = &Relay_2;

    SerialXBee.begin(Configure.GetParamInt("XBee", "BaudRate"));
    ClearInputQueue();
    NoLineWaiting();
    Infrastructure.XBeeActive = true;
    SetCrumb(crumbXBee, 238);
  } // tXBee::setup()

 void tXBee::TransmitToBase(const char BoardTag,               // peripheral XBee
                            const tBoardSlot BoardSlot,
                            const char Payload[],
                            const unsigned long Checksum)
   { // char Message[100];
     // sprintf(Message, "  XBee.TransmitToBase('%c', '%d', '%s', %lu)",
     //                    BoardTag, BoardSlot, Payload, Checksum );
     // Infrastructure.SayInIDE(Message);

     if (Infrastructure2.SendingToBase)
       { SerialXBee.printf("%c\t%d\t%s\t%lu\n",
                           BoardTag, BoardSlot, Payload, Checksum);
       }
   } // tXBee::TransmitToBase
