#include <stdarg.h>
#include <avr/pgmspace.h>

//
// Makro um Tipparbeit bei der Übergabe der Formatstrings an die printSerial() Funktion
// zu sparen. Es muss dort CF("Formatstring") statt F("Formatstring") verwendet werden.
// Durch das Makro erspart man sich die notwendige Angabe des (const char*) Ausdrucks
//
#define CF(TXT) ((PGM_P)F(TXT))

//
// Definition Textkonstanten
//
const char txt00[] PROGMEM = " ";
const char txt01[] PROGMEM = ",";
const char txt02[] PROGMEM = "ab";
const char txt03[] PROGMEM = "auf";
const char txt04[] PROGMEM = "gerundet";
const char txt05[] PROGMEM = "minus";
const char txt06[] PROGMEM = "Max";
const char txt07[] PROGMEM = "Min";
const char txt08[] PROGMEM = "negativ";
const char txt09[] PROGMEM = "plus";
const char txt10[] PROGMEM = "positiv";
const char txt11[] PROGMEM = "Abstand";
const char txt12[] PROGMEM = "Anzahl";
const char txt13[] PROGMEM = "Fehler";
const char txt14[] PROGMEM = "Korrektur";
const char txt15[] PROGMEM = "Regel";
const char txt16[] PROGMEM = "Step";
const char txt17[] PROGMEM = "Zahl";
const char txt18[] PROGMEM = "Zahn";

//
// Zeigerarray auf die Textkonstanten
//
const char* const pTxt[] = {txt00,txt01,txt02,txt03,txt04,txt05,txt06,txt07,txt08,txt09,txt10,txt11,txt12,
                            txt13,txt14,txt15,txt16,txt17,txt18};

//
// Anonymes enum für die INDEX Bezeichner zur besseren Lesbarkeit des
// Programmcodes
//
enum {SPACE,KOMMA,AB,AUF,GERUNDET,MINUS,MAX,MIN,NEGATIV,PLUS,POSITIV,ABSTAND,ANZAHL,FEHLER,
      KORREKTUR,REGEL,STEP,ZAHL,ZAHN};

//
// Forwarddeklaration für die Funktion zur seriellen Ausgabe 
// Diese Funktion hat eine variabe Parameterliste
//
void serialPrint(const char*, ...);

//
// Hauptprogramm: Ausgabe der Textkombinationen
//
void setup() {
  Serial.begin(115200);

  unsigned int zahn = 140;
  float wert = 7.2345;
  serialPrint(CF("%s%s\n"), pTxt[AB], pTxt[GERUNDET]);
  serialPrint(CF("%s%s\n"), pTxt[AUF], pTxt[GERUNDET]);
  serialPrint(CF(" %s%s\n"), pTxt[KORREKTUR], pTxt[ZAHN]);
  serialPrint(CF(" %s%s %s\n"), pTxt[KORREKTUR], pTxt[ZAHN], pTxt[POSITIV]);
  serialPrint(CF(" %s%s %s\n"), pTxt[KORREKTUR], pTxt[ZAHN], pTxt[NEGATIV]);
  serialPrint(CF(" %s%s\n"), pTxt[REGEL], pTxt[ZAHN]);
  serialPrint(CF(" %s%s%s\n"), pTxt[KORREKTUR], pTxt[ZAHN], pTxt[ABSTAND]);
  serialPrint(CF(" %s%s%s\n"), pTxt[REGEL], pTxt[ZAHN], pTxt[ABSTAND]);
  serialPrint(CF("%s berechnet\n"), pTxt[ABSTAND]);
  serialPrint(CF("nur gleicher %s\n"), pTxt[ABSTAND]);
  serialPrint(CF("%s%s\n"), pTxt[ABSTAND], pTxt[MAX]);
  serialPrint(CF("%s%s\n"), pTxt[ABSTAND], pTxt[MIN]);
  serialPrint(CF("%s %s\n"), pTxt[ABSTAND], pTxt[PLUS]);
  serialPrint(CF("%s %s\n"),pTxt[ABSTAND], pTxt[MINUS]);
  serialPrint(CF("%s%s\n"), pTxt[ABSTAND], pTxt[FEHLER]);
  serialPrint(CF("%s%s soll\n"), pTxt[STEP], pTxt[ZAHL]);
  serialPrint(CF("%s%s ist\n"), pTxt[STEP], pTxt[ZAHL]);
  serialPrint(CF("%ss zu viel\n"), pTxt[STEP]);
  serialPrint(CF("%ss zu wenig\n"), pTxt[STEP]);
  serialPrint(CF("%s%ss: %d\n"), pTxt[ZAHN], pTxt[STEP],25001);
  serialPrint(CF("%s%s-Nr.: %d\n"), pTxt[ZAHN], pTxt[STEP], zahn);
  serialPrint(CF("Ein Bruch: %f%% wird hier angezeigt.\n"), wert);
}

void loop() {
  // put your main code here, to run repeatedly:

}

//
// Ausgabe von Strings, Integer (Ganzzahlen) oder Double/Float (Fließkommazahlen), 
// die in der variablen Parameterliste übergeben werden.
// %s ist das Formatzeichen für Strings, 
// %d für Integer (Ganzzahlen) 
// %f für Fließkommazahlen. 
// Das Steuerzeichen "\n" erzeugt einen Zeilenumbruch bei der Ausgabe.
//
void serialPrint(const char* fString, ...) {
  va_list pArgListe;
  int intVal;
  double dblVal;
  char* pText;
  char fZ, z;

  va_start(pArgListe, fString);
  while((fZ = pgm_read_byte_near(fString))) {
    if (fZ != '%') {
      Serial.print((char)pgm_read_byte_near(fString));
    } else {
      switch((fZ = pgm_read_byte_near(++fString))) {
        case 's': pText = va_arg(pArgListe, char*);
          while ((z = pgm_read_byte_near(pText++)) != 0 ) {
            Serial.print(z);
          };
          break;
        case 'd': intVal = va_arg(pArgListe, int);
          Serial.print(intVal);
          break;
        case 'f': dblVal = va_arg(pArgListe, double);
          Serial.print(dblVal);
          break;
        default:
         Serial.print((char)pgm_read_byte_near(fString)); 
         break;
      }
    }
    fString++;
  }
  va_end(pArgListe);
}