#include <math.h>

#define GESAMTSTEPS 2048000U 

//
// Struktur für Zahnrad Daten
struct stepDaten {
  unsigned long gesamtSteps;
  unsigned int errGesamtSteps = 0;
  unsigned int zaehne;
  unsigned int zahnZaehler = 0;
  unsigned long stepsRegelZahn;
  unsigned long stepsErfolgt;
  int gesamtFehler;
  int korrekturWert;
  unsigned long korrekturStep;
  unsigned long korrekturOberGrenze;
};

struct statDaten {
  unsigned long sumGesamtZaehne = 0;
  unsigned long sumRegelZaehne = 0;
  unsigned long sumPosKorrekturZaehne = 0;
  unsigned long sumNegKorrekturZaehne = 0;
};

struct stepDaten zahnrad;   // Modulglobal definieren
struct statDaten statistik;

void setup() {

  Serial.begin(115200);
  for (unsigned int i = 10; i<= 140; i++ ) {
    Serial.println("");
    Serial.println("---------------------");
    Serial.println("Nächstes Zahnrad");
    Serial.println("---------------------");
    Serial.println(""); 
    initZahnrad(i);
    Serial.print("Zahnrad Anzahl Zähne: ");
    Serial.println(i);
    Serial.print("GesamtFehler        : ");
    Serial.println(zahnrad.gesamtFehler);
    Serial.print("Steps pro Zahn      : ");
    Serial.println(zahnrad.stepsRegelZahn);
    Serial.print("Erste Regelkorrektur nach: ");
    Serial.println(zahnrad.korrekturOberGrenze + zahnrad.stepsRegelZahn);
    //zeigeStatistik();

    for (unsigned int i=0; i<zahnrad.zaehne;++i) {
      unsigned long stepsProZahn = naechsteSchrittFolge();
      zahnrad.stepsErfolgt += stepsProZahn;
      
      Serial.print("Summe Steps: ");
      Serial.print(zahnrad.stepsErfolgt);
      Serial.print(" Steps/Zahn: ");
      Serial.print(stepsProZahn);
      Serial.print(" Zahn Nr.: ");
      Serial.print(zahnrad.zahnZaehler);
      if (zahnrad.zahnZaehler < 10) Serial.print("\t");
      Serial.print("\t");
      int korrektur = stepsProZahn - zahnrad.stepsRegelZahn;
      if (korrektur > 0) {
        Serial.print("+");
      } else if (korrektur == 0) {
        Serial.print(" ");
      }
      Serial.println(korrektur);
    }
    // Zahnrad fertig abgearbeitet
    if (zahnrad.stepsErfolgt != GESAMTSTEPS) {
      zahnrad.errGesamtSteps += 1;
    }
  }
  Serial.println("\nStatistik:");
  zeigeStatistik();
}

//
// Initialisieren der globalen Zahnrad Struktur
//
void initZahnrad(unsigned int zaehne) {
  zahnrad.gesamtSteps = GESAMTSTEPS;
  zahnrad.zaehne = zaehne;
  zahnrad.zahnZaehler = 0;
  zahnrad.stepsRegelZahn = round((double)zahnrad.gesamtSteps / zahnrad.zaehne);
  zahnrad.stepsErfolgt = 0;
  zahnrad.korrekturWert = 1;
  zahnrad.gesamtFehler = (long)zahnrad.stepsRegelZahn * zahnrad.zaehne - zahnrad.gesamtSteps;
  
  if (zahnrad.gesamtFehler) {
    if (zahnrad.gesamtFehler < 0) {
      zahnrad.gesamtFehler *= (-1);
      zahnrad.korrekturWert= -1;
    } 
    zahnrad.korrekturStep = round((double)zahnrad.gesamtSteps / zahnrad.gesamtFehler);
    zahnrad.korrekturOberGrenze = zahnrad.korrekturStep;
  } else {
    zahnrad.korrekturStep = 0;
    zahnrad.korrekturOberGrenze = 0;
  }
  aktualisiereStatistik();
}

//
// Ermitteln der notwendigen Steps pro Zahnrad
// Bruchwerte werden durch "Korrektur-Steps" ausgeglichen
//
unsigned long naechsteSchrittFolge(void) {
  unsigned long erg;

  if (zahnrad.zahnZaehler < 1 && zahnrad.korrekturStep)  {
    erg = zahnrad.stepsRegelZahn - zahnrad.korrekturWert;
    Serial.print("Ks-> "); 
  } else if (zahnrad.stepsErfolgt >= zahnrad.korrekturOberGrenze && zahnrad.korrekturStep) {
    erg = zahnrad.stepsRegelZahn - zahnrad.korrekturWert;
    zahnrad.korrekturOberGrenze += zahnrad.korrekturStep;
    Serial.print("Kr-> ");
  } else {
    Serial.print("     ");
    erg = zahnrad.stepsRegelZahn;
  }
  zahnrad.zahnZaehler++;
  return erg;
}

void aktualisiereStatistik() {
  statistik.sumGesamtZaehne += zahnrad.zaehne;
  statistik.sumRegelZaehne += (zahnrad.zaehne - zahnrad.gesamtFehler);
  // Ein Korrekturzahn wird als positiv gezählt, wenn er einen Step mehr hat
  // als ein Regelzahn. Ein neg. Korrekturzahn hat dementsprechend einen Step weniger
  // als ein Regelzahn.
  if (zahnrad.korrekturWert < 0) {
     statistik.sumPosKorrekturZaehne += zahnrad.gesamtFehler;
  }else {
      statistik.sumNegKorrekturZaehne += zahnrad.gesamtFehler;
  }
}

void zeigeStatistik() {
  Serial.print("Gesamtzahl berechneter Zähne        : ");
  Serial.println(statistik.sumGesamtZaehne);
  Serial.println("--------------------------------------------");
  Serial.print("Summe Regelzähne                    : ");
  Serial.println(statistik.sumRegelZaehne);
  Serial.print("Summe pos. Korrekturzähne           : ");
  Serial.println(statistik.sumPosKorrekturZaehne);
  Serial.print("Summe neg. Korrekturzähne           : ");
  Serial.println(statistik.sumNegKorrekturZaehne);
  Serial.println("--------------------------------------------");
  Serial.print("Summe aus Regel- und Korrekturzähnen: ");
  Serial.println(statistik.sumRegelZaehne+statistik.sumPosKorrekturZaehne+statistik.sumNegKorrekturZaehne);
  Serial.println("--------------------------------------------");
  Serial.print("Fehler Gesamtsteps  (muss 0 sein)   : ");
  Serial.println(zahnrad.errGesamtSteps);
}

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

}