/*
   AutoType Keyboard for the Raspberry PICO

  NOTE: This WokWi sketch uses Serial1 instead of Keyboard.

  PICO pins:-

  USB
  (GP0)(GP1)[GND](GP2)(GP3)(GP4)(GP5)[GND](GP6)(GP7)(GP8)(GP9)[GND](GP10)(GP11)(GP12)(GP13)[GND](GP14)(GP15)

   Use links / slide switches to select a feature
   GP2  - SEND \N - send ENTER, it de-focused the input area, so allow it to be disabled.
   GP6  - SLOW    - strap low for faster typing
   GP9  - HALT    - The Pico can be reprogrammed using BOOTSEL, so this is not essential
   GP10 - TYPING  - assert low when typing, Only start typing if high.

   Press the HALT button to set the PAUSE period. This is the time from GA to starting Typing.

   Overview:-
   I needed to automate typing a conversation, between two people over an App like a Chat Bot session.
   The App needed a slow stream of repeatable text to soak test it and fill up scroll buffers, over many minutes.
   It needed a trickle of characters as if typed on the App's keyboard(s).

   I can plug in an external keyboard at each end.
   The Arduino IDE has Keyboard examples.

   Raspberry PICO were available from Farnell for about 6 GBP.
   There are examples using them as USB Keyboards.

   It is impractical to read the display, but a wire can be used between two of these devices, so they can type in turn.

   Typically, Person A: types to Person B: and B: types back.

   This version allows a PAUSE to be set , from ending typing to wait. 
   This can be set using the HALT button. Press it for the required PAUSE duration.

   Raspberry PICO were available from Farnell for about 6 GBP.
   There are examples using them as USB Keyboards.

   See: http://ccgi.dougrice.plus.com/cgi-bin/wiki.pl?AutoType

   It is easy to get the Arduino IDE to work.

   https://www.arduino.cc/
   https://www.arduino.cc/reference/en/language/functions/usb/keyboard/
   https://www.arduino.cc/en/Tutorial/BuiltInExamples/KeyboardMessage

   Add Arduino "MBED OS RP2040 boards"
   Based on Keyboard example
   With "Arduino MBED OS RP2040 Boards 3.3.0" installed you can generate .uf2 files for upload to the PICO
   https://github.com/arduino/ArduinoCore-mbed
   https://github.com/arduino/ArduinoCore-mbed/tree/master/libraries/USBHID/examples

   Example:

  #include "PluggableUSBHID.h"
  #include "USBKeyboard.h"

  USBKeyboard Keyboard;
  void setup() {
  // put your setup code here, to run once:
  }

  void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
  Keyboard.printf("Hello world\n\r");
  }

  ========================================================
   SEC: 1.0 -  Design Brainstorm and thoughts:-
  ========================================================
  I needed to automate typing a conversation, between two people over an App like a Chat Bot session.

  The App needed a slow stream of repeatable text to soak test it and fill up scroll buffers, over many minutes.

  It needed a trickle of characters as if typed on the App's keyboard(s).

  I can plug an external keyboard at each end, which could be two of these PICOs. This is not essential.
  It is impracticle to read the display, but a wire can be used between two of these devices, so they can type in turn.

  Typically, Person A: types to Person B: and B: types back.

    timeline:-
    A:   | typing |GA| PAUSE | waiting   | typing    |GA| PAUSE | waiting   | typing
    B:   | waiting   | typing |GA| PAUSE | waiting      | typing |GA| PAUSE | waiting

    A: types pressing keys on a keyboard, at up to 10 characters a second or slower
    A: hands over to B: by sending "GA" to Go Ahead, then waits
    B: reads the typing and can type but should wait until A: stops typing, before starting.
    B: types and A: waits til B: stops.

    PAUSE is set by holding HALT low.

    Typing on a smart phone soft keyboard is slower.

    some devices display both conversations in a single line of text, making it difficult to read.

   Use a one PIN Wired Or BUS wire to communicate between two of these PICOs.

     Assert Low when Typing,
     Stop typing when "\n" found - Users send GA for Go Ahead
     Wait for it to go high, before sarting to type.

             pull up
              |
   -----+-----+-----+----------+--- Wired OR buss - Assert low when typing.
        |           |          |
      [ A ]       [ B ]     [ SW ]

     [ A ] and [ B ] Wait for high to start typing and assert a low

     [SW] is a switch which can pull low to inhibit typing.
     on start up hold off typing.

*/

/*========================================================
   SEC: 1.0 - Configuration
  ======================================================*/
#define TYPING_INTER_KEY_DELAY 20
//
// there was a bug with the SmartPhone App
// Pressing ENTER looses focus, so suppress ENTER
// select if "\n" presses ENTER key
//
#define nNOENTER true
#define NOENTER false

/*
   This code can use a Raspberry PICO or RP2
   Arduino IDE Tools configuration

   RP2 PICO
    Board: Raspberry Pi Pico
    Programmer: N/A - ensure  bootsel button is pressed when you plug in PICO
*/
/*
  #include "PluggableUSBHID.h"
  #include "USBKeyboard.h"

  USBKeyboard Keyboard;
*/

#define NOENTER 2
#define SLOW 6
#define PAUSE 8 
#define HALT 9
#define TYPING 10
/*
  PICO pins:-

  USB
  (GP0)(GP1)[GND](GP2)(GP3)(GP4)(GP5)[GND](GP6)(GP7)(GP8)(GP9)[GND](GP10)(GP11)(GP12)(GP13)[GND](GP14)(GP15)

   Use links / slide switches to select a feature
   GP2  - SEND \N - send ENTER, it de-focused the input area, so allow it to be disabled.
   GP6  - SLOW    - strap low for faster typing
   GP9  - HALT    - The Pico can be reprogrammed using BOOTSEL, so this is not essential
   GP10 - TYPING  - assert low when typing, Only start typing if high.
*/

/*========================================================
   SEC: 2.0 - Typing Text
  ======================================================*/

#define PHONE
/* Phone User typing - The \n indicate end of sentence.*/
#ifdef PHONE
int phone = true;

// send space followed by a space.
// The phone looses focus when the  ENTER key is pressed.
// It buffers text and waits for a pause. send some single spaces or double spaces.
// A user could send GA to hand over to the other End
// A \n is used to signal end of typing.

char myString[] = "  This is BeeTea Engineer, DHR, making a test call.\n "
                  " Please hang up the B-leg \n "
                  " I am testing  the new Typing UK app  \n "
                  " Hello Doug here  \n "
                  " Can   you see my typing?  \n "
                  " The Quick brown fox jumps over the lazy dog  \n "
                  " I am testing the    new Relay UK app \n "
                  //" 1 2 3 4 5 6 7 8 9 ! £ $ % ^ & * ( ) _- + = { } [ ] ~ # : ; @ ' < > , . / ?"
                  ;

#else
int phone = false;
/* Agent typing - The \n indicate end of sentence. */
char myString[] = " Welcome  to Chit  Chat UK   \n "
                  " Please  type your  name?   \n  "
                  " You are talking  to Jack  \n "
                  " How May I help you today  \n "
                  " Okay I   will type  back?   \n  "
                  " The Quick  brown  fox jumps  over the  lazy dog  \n "
                  " Hello  Caller the  other party has cleared.  \n "
                  " \n ";

#endif

/*========================================================
   SEC: 3.0 - Variables
  ======================================================*/

int slowSpeed = true;
int waiting = true;
int waitingPauseCountDown = 0;

int waitingPauseSet = 0; // setup while HALT pressed.

int lastHALT = 1;    // shift register button detector.


// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTs = 0;  // the last timestamp

char * ptr;

/*========================================================
   SEC: 4.0 - Functions
  ======================================================*/

void setup() {
  Serial1.begin(9600);

  // don't need to set anything up to use DigiKeyboard
  ptr = myString;

  pinMode( NOENTER, INPUT_PULLUP ); // suppress \n <ENTER>
  pinMode( SLOW, INPUT_PULLUP ); // speed
  pinMode( PAUSE, INPUT_PULLUP );
  pinMode( HALT, INPUT_PULLUP );
  pinMode( TYPING, INPUT_PULLUP );  // used for WiredOR
  pinMode( LED_BUILTIN, OUTPUT );

  waiting = true;
  Serial1.println(" hello , Use slide switches to select features\n");
  Serial1.println(" _/ _ use CRLRF\n _/ _ speed\n _/ _ HALT\n _/ _ typing\n");

  lastHALT=digitalRead( HALT );

}

void polledTimeSlice() {
  unsigned long now = millis();  // the last timestamp

  
  // using unsigned so be caureful.
  if ( now - lastTs > 300 ) {
    // 0.1 second tick
    lastTs = now;

    //
    // payload - run every 300 ticks.
    //

    // if typing TYPING asserted Low, wait for it to go high
    // If input goes low turn off waiting
    //if ( ! digitalRead( IP ) ){
    if ( digitalRead( TYPING ) ) {
      waiting = false;
    }
    // shift register edge detector
    lastHALT = ( lastHALT + lastHALT +digitalRead( HALT ) ) & 3 ;

      // edge ---___ reset waitingPauseSet
      // edge ___--- store waitingPauseSet
      // ____ increment.  
    switch (  lastHALT ) {
      case 0: {  waitingPauseSet ++ ; break; };
      case 1: {
        Serial1.print( " waitingPauseSet: " );  
        Serial1.print( waitingPauseSet ); break; 
      };
      case 2: {  waitingPauseSet = 0; break; };

      default:{  }
    }  
  }
}

void LED( int on ) {
  /* IF on == TRUE then Typing. Assest TYPING LOW using Wired OR */

  if ( on ) {
    // ASSERT LOW
    pinMode( LED_BUILTIN, OUTPUT );
    pinMode( TYPING, OUTPUT );
    // Wired OR
    digitalWrite( LED_BUILTIN, on );
    digitalWrite( TYPING, LOW );
  } else {
    pinMode( LED_BUILTIN, OUTPUT );
    // to turn on a PULLUP make pin input
    pinMode( TYPING, INPUT_PULLUP );
    digitalWrite( LED_BUILTIN, on );
  }
}

/*========================================================
   SEC: 5.0 - Functions -  delay
  ======================================================*/

void delay_( unsigned long ms ) {
  delay( ms );
}



void wait() {
  // pick a random delay and square it to make it more like real typing?
  //delay(random( 0, TYPING_INTER_KEY_DELAY )*random( 0, TYPING_INTER_KEY_DELAY ));
  if ( digitalRead( SLOW ) == 1 ) {
    delay( random( 50, 250 ) );
  } else {
    delay( random( 0, 100 ) );
  }
}

void waitLong() {
  int count;
  delay( random( 1000, 2000 ) );
}

/*========================================================
   SEC: 6.0 - Functions Increment  & Type Key
  ======================================================*/

void incStringPtr() {
  unsigned long ts = 0;

  /* end of string return to start.*/
  ptr ++ ;

  // if space end of word.
  if ( ptr[0] == ' ') {
  }

  // END OF LOOP
  if ( ptr[0] == '\0') {
    ptr = myString;
    if ( slowSpeed ) {
      slowSpeed = false;
    } else {
      slowSpeed = true;
    } 
    ts = millis();
    /*
      Keyboard.printf( "%s", " ts: " );
      Keyboard.printf( "%d", ts / 1000 );
      Keyboard.printf( "%s", " seconds " );
    */
  }
}

void typeKey( char letter ) {
  // enable typing if HALT is high in this version.
  if ( digitalRead( HALT ) == 1 ) {
    // this is generally not necessary but with some older systems it seems to
    // prevent missing the first character after a delay:
    // DigiKeyboard.sendKeyStroke(0);
    // if ( NOENTER && isControl( letter ) ) {
    // Read GP2 to select sending \n
    if ( isControl( letter ) && digitalRead( NOENTER ) ) {
      letter = ' ';
    }
    //Keyboard.printf( "%c", letter );
    Serial1.print( letter );
    incStringPtr();
  }
}
/*


*/

/*========================================================
   SEC: 7.0 - Functions Main Loop
  ======================================================*/

void loop() {
   delay(1); 
  polledTimeSlice();

  // look for HALT ___----
  // Store 
  if (  digitalRead( HALT ) == 1 ) {


  }   

  /* work through string and print character by character */
  /* check if input low and enable typing. */
  if ( waiting ) {
    return;
  }

  if ( waitingPauseCountDown > 0 ) {
    waitingPauseCountDown --;
    if ( waitingPauseCountDown > 0 ) {
      waiting = true;
      //Serial1.print( "!" );
    }
    return;
  }

  // look for end of sentence,
  if ( isControl( ptr[0] ) ) {

    // turn off LED at end of sentence
    // This is used to tell other end as well.
    LED( LOW );

    // end of sentence turn os Waiting flag
    // so loop waits for other end to finish sentence.
    //waiting = true;
    waitingPauseCountDown = waitingPauseSet ; // set up by holding HALT down.
    //Serial1.print( "!" );
    typeKey( ptr[0] );
    wait();
  } else {
    LED( HIGH );

    /* type character */
    typeKey( ptr[0] );
    if ( slowSpeed ) {
      wait();
    }
    wait();
  }

  if ( isWhitespace( ptr[0] ) ) {
    wait();
  }
}

/*========================================================
   SEC: 8.0 - Examples
  ======================================================*/
/*
  This is BeeTea Engineer, DHR, making a test call.
    Please hang up the B-leg
      I am testing  the new Relay UK app
        Hello Doug here
          Can   you see my typing?
            The Quick brown fox jumps over the lazy dog
              I am testing the    new Relay UK app
                ts: 60 seconds   This is BeeTea Engineer, DHR, making a test call.
                  Please hang up the B-leg
                    I am testing  the new Relay UK app
                      Hello Doug here

*/
/*========================================================
   SEC: 9.0 - End
  ======================================================*/
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT