// https://forum.arduino.cc/t/can-someone-explain-this-code-trying-to-learn/1031873
// https://wokwi.com/projects/342871593282699859

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

# define PIXEL_PIN    6
# define NLAMPS 20  // tank display size

Adafruit_NeoPixel tank(NLAMPS, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

// tank model stuff

# define FULL_TANK  ((NLAMPS - 1) * 32 + 31)  // mixed point tank level

# define TANK_FILL_RATE A0  // analog input for tank fill rate
# define FILL_ON   A1       // switch controls filling of tank

# define UPPER_FLOAT  5   // outputs from level sensors - use as inputs to logic
# define LOWER_FLOAT  4

// pump stuff

# define NPUMPS 2
unsigned char pumpState[NPUMPS] = {0, 0};

const unsigned char pumpRelay[NPUMPS] = {9, 8};   // pump relays
const unsigned char pumpButton[NPUMPS] = {3, 2};   // pump manual controls

void setup() {
  Serial.begin(115200);
  Serial.println("Hello Tank World!\n");

  setupRETCON();

  tank.begin();  // neopixel

  pinMode(FILL_ON, INPUT_PULLUP);

  pinMode(UPPER_FLOAT, OUTPUT);
  pinMode(LOWER_FLOAT, OUTPUT);

  for (unsigned char ix = 0; ix < NPUMPS; ix++) {
    pinMode(pumpButton[ix], INPUT_PULLUP);

    pinMode(pumpRelay[ix], OUTPUT);
    digitalWrite(pumpRelay[ix], LOW);
  }

  tank.show();
}

void loop()
{
// run the model tank and pumps thing
  tankPumpWorld();
  printStatus();

// below here you have full speed loop
// here is just a simple empty/fill example:

  loopRETCON();
}

// handle tank and pump simulation.
int tankLevel;

void tankPumpWorld() {
  static unsigned long lastLoop; 
  unsigned long now = millis(); //grab current time 

  if (now - lastLoop < 50) return;    // 20 Hz all around
  lastLoop = now;

// tank simulation and manual controls
// flawed  checkPumpSwitches();

  tankSimulate();   // update the tank model
  displayTank();
}

// system status report
void printStatus()
{
  static unsigned long lastReport;
  unsigned long now = millis();

  if (now - lastReport < 1333) return;
  lastReport = now;

  unsigned char tankFull = digitalRead(UPPER_FLOAT);    // just read the status lamp!
  unsigned char tankEmpty = !digitalRead(LOWER_FLOAT);    // just read the status lamp!


  if (tankFull) Serial.print("tank is full ");
  if (tankEmpty) Serial.print("tank is empty ");

  if (pumpState[0]) Serial.print("pump one is ON ");
  if (pumpState[1]) Serial.print("pump two is ON ");

  if (!digitalRead(FILL_ON)) {
    Serial.print(" the tank is filling at ");
    Serial.print((analogRead(TANK_FILL_RATE) >> 6) + 1);
    Serial.print("  units per tick.");
  }
  else Serial.print(" the fill valve is closed.");

  Serial.print(" tank level "); Serial.println(tankLevel);
}

// fill and empty the tank.
void tankSimulate()
{
  int rate = (analogRead(TANK_FILL_RATE) >> 6) + 1;

//  Serial.println(rate); 1 to 16 units per tick, fine

  if (digitalRead(FILL_ON) == LOW)
    addToTank(rate);

  runPumps();
}

void displayTank()
{
  int tankI = tankLevel >> 5;

  for (unsigned char ix = 0; ix < NLAMPS; ix++)  tank.setPixelColor(ix, 0);

  if (tankLevel)
    for (unsigned char ix = 0; ix <= tankI; ix++)  tank.setPixelColor(ix, 0x0000ff);

  digitalWrite(UPPER_FLOAT, tankI == 19);
  digitalWrite(LOWER_FLOAT, tankLevel > 0 ? HIGH : LOW);
  
  tank.show();
}

// turn a pump on or off - logic
void switchPump(unsigned char thePump, unsigned char onOff)
{
  pumpState[thePump] = onOff;
}

// get sgtate of pump running/not running
unsigned char pumpIsOn(unsigned char thePump)
{
  return pumpState[thePump];
}

// turn off or on the pumps - mechanical. subtract from tank.
// heed the pushbutton manual pump control!
void runPumps()
{
for (unsigned char ix = 0; ix < NPUMPS; ix++)
  if (pumpState[ix] || !digitalRead(pumpButton[ix])) {
    addToTank(-5);
  }

}

// add or subtract from the tank. limit 0..full
void addToTank(int units)
{
  tankLevel += units;

  if (tankLevel > FULL_TANK) tankLevel = FULL_TANK;
  if (tankLevel < 0) tankLevel = 0;
}

// spam message suppression
void printOnce(char *msg)
{
  static char message[32] = {0};

  if (strcmp(message, msg)) {
    Serial.println(msg);
    strcpy(message, msg);
  }
}








// unused parts from development

// press the button, turn on/leave on pump
// flawed - interferes with another pump on/off logic
void checkPumpSwitchesNOTUSED1()
{
  for (unsigned char ix = 0; ix < NPUMPS; ix++) {
    if (pumpIsOn(ix)) {
      if (digitalRead(pumpButton[ix]))
        switchPump(ix, 0);
    }
    else {
      if (!digitalRead(pumpButton[ix]))
        switchPump(ix, 1);
    }
  }
}

// OLD: press the button, toggle the coresponding pump on/off
void checkPumpSwitchesNOTUSED0()
{
  static unsigned char lastPumpButton[NPUMPS] = {HIGH, HIGH};

  for (unsigned char ix = 0; ix < NPUMPS; ix++) {
    unsigned char thisPumpButton = digitalRead(pumpButton[ix]);
  
    if (lastPumpButton[ix] != thisPumpButton) {
      if (!thisPumpButton) {
        Serial.print("      saw button "); Serial.println(ix);
        switchPump(ix, pumpIsOn(ix) ? 0 : 1);
      }
      lastPumpButton[ix] = thisPumpButton;
    }
  }
}


// test loop with simple logic?
/* test patterns for tank simulator
// read the model
  unsigned char tankFull = digitalRead(UPPER_FLOAT);    // just read the status lamps!
  unsigned char tankEmpty = !digitalRead(LOWER_FLOAT);

// and use switchPump(pumpNumber, onOrOff) to control the pumps

  if (tankFull)         // if the tank is full
    switchPump(0, 1);   // start pump 0

  if (tankEmpty)        // if the tank is empty
    switchPump(0, 0);   // stop pump 0
*/