/*
 * loop scanner infrastructure
 */

#include "loopScanner.h"

// module-local variables for loop scanner

typedef struct {
  int maxdiv;
  int divider;
  DFuncParPtr loopfunc;
  int funcparam;
  String info;
} LoopScanner;

LoopScanner loopScanners[DIVNUM];
int numOfLoopScanners = 0;
int loopmicros = 0;
int oldmicros = micros();
int oldmillis = millis();

int getLoopMicros() {
  return loopmicros;
}

/*
 * the loopScanner() is called once every Arduino-loop(), i.e. periodically, but not in dependable intervals
 * therefore, it performs its logic only if one milliseconds has passed
 * the basic tick of one millisecond is further divided down by a set of loopScanners
 * each loopScanner has a configured period in millisecnds. If this period has expired, its "loop function" is executed
 * it keeps tabs on loop()-period by maintaining the current loop time in microseconds in global variable loopmicros
 * global variable loopmicros can be inspected on serial interface using the ".t" command
 */
void loopScanner() {
  int ts = micros();
  loopmicros = ts - oldmicros;
  oldmicros = ts;
  ts = millis();
  if (oldmillis==ts) return;                            // only proceed every millisecond, if no full millisecond since last call, simply do nothing and return
  oldmillis = ts;
  long smicros, emicros;
  for (int dn=0; dn<numOfLoopScanners; dn++) {
    loopScanners[dn].divider++;
    if (loopScanners[dn].divider>=loopScanners[dn].maxdiv) {
      loopScanners[dn].divider = 0;
      smicros = micros();
      loopScanners[dn].loopfunc(loopScanners[dn].funcparam);
      emicros = micros();
      if ((emicros-smicros)>(loopScanners[dn].maxdiv*1000)) {
        SERIAL_PRINTF("loopScanner(structVariant) %d - %s - maxdiv %d expired, executing function took %d microseconds\n",dn,loopScanners[dn].info.c_str(),loopScanners[dn].maxdiv,emicros-smicros);
      }
    }
  }    
}

void initLoopScanner() {
  numOfLoopScanners = 0;
  oldmillis = millis();
  oldmicros = micros();
  SERIAL_PRINTLN("loopScanner initialized");
}

void printLoopScanner(int i) {
  if (i>=numOfLoopScanners) {
    SERIAL_PRINTF("cannot print loopScanner number %d, there are only %d loopScanners registered\n",i,numOfLoopScanners);
  } else {
    SERIAL_PRINTF("[loop scanner(structVariant) #%03d: FUNC=0x%08X - INFO=%s - DIV=%06d - PAR=%d]\n",i,loopScanners[i].loopfunc,loopScanners[i].info.c_str(),loopScanners[i].maxdiv,loopScanners[i].funcparam);   
  }
}

void printLoopScanners() {
  for (int i = 0; i<numOfLoopScanners; i++) {
    printLoopScanner(i);
  }
}

extern void addLoopScanner(int divider, DFuncParPtr scannerFunc, int funcParameter, String info) {
  if (numOfLoopScanners>=DIVNUM) return;
  loopScanners[numOfLoopScanners].divider = 0;
  loopScanners[numOfLoopScanners].maxdiv = divider;
  loopScanners[numOfLoopScanners].loopfunc = scannerFunc;
  loopScanners[numOfLoopScanners].funcparam = funcParameter;
  loopScanners[numOfLoopScanners].info = String(info);
  SERIAL_PRINTF("loop scanner #%03d added: ",numOfLoopScanners);
  numOfLoopScanners++;
  printLoopScanner(numOfLoopScanners-1);
}