// C++ code


/* from wokwi docs

#define I2C_ADDR    0x27
#define LCD_COLUMNS 20
#define LCD_LINES   4

LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);

void setup() {
  // Init
  lcd.init();

*/

//# include <Adafruit_LiquidCrystal.h>

# include <LiquidCrystal_I2C.h>

# define I2C_ADDR    0x27
# define LCD_COLUMNS 20
# define LCD_LINES   4

int balance = 0;
int waterCost = 5;
int foamCost = 5;

// Pin and Button for coins, replacement for the coin slot
int coinPin_1 = 2;
int coinPin_5 = 3;
int coinPin_10 = 4;

// Pin and Button for water and foam function
int waterPin = 5;
int foamPin = 6;

// Pin for relay
int relayPin = 9;

// Event flag for water and foam feature
bool isWatering = false;
bool isFoaming = false;
bool hasWaterTime = false;
bool hasFoamTime = false;

// Variables for toggle
int waterButtonState = 0;
int prevWaterButtonState = 0;
unsigned long toggleTime = 0;
unsigned long debounce = 200UL;

// Millis() variables
unsigned long currentWaterMillis;
unsigned long currentFoamMillis;
unsigned long startWaterMillis;
unsigned long startFoamMillis;

const unsigned long second = 1000UL;
const unsigned long minute = second * 60;
const unsigned long waterTime = second * 20; //... = minute * 2; life too short
const unsigned long foamTime = second * 13;  //... = minute;
unsigned long waterTimeLeft = waterTime;
unsigned long foamTimeLeft = foamTime;

//Adafruit_LiquidCrystal lcd(0);
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);

void setup()
{
  // Serial
  Serial.begin(115200);
  Serial.println("\nJello Whirled!\n");

  //Intialize the LCD
  lcd.init();
  
  //Set the pin of coinButtons
  pinMode(coinPin_1, INPUT_PULLUP);
  pinMode(coinPin_5, INPUT_PULLUP);
  pinMode(coinPin_10, INPUT_PULLUP);
  // Set the pin of water and foam button
  pinMode(waterPin, INPUT_PULLUP);
  pinMode(foamPin, INPUT_PULLUP);
  
  // Set the pin of relay
  pinMode(relayPin, OUTPUT);
}

# define PRESST  LOW
# define NOTPRESST HIGH

void loop()
{
  // Read and add inserted coin
  balance = AddCoinTo(balance);
  
  // Balance constraint
  if(balance < 5)
  {
    if(!hasWaterTime)
    {
      DisplayInsert();
      DisplayBalance(balance);
    }
    if(digitalRead(waterPin) == PRESST || digitalRead(foamPin) == PRESST)
    {
      Serial.println("WTF LCD");
      // clear the display
      lcd.clear();
      lcd.home();
      lcd.print(" INSUFF BALANCE ");
    }
  }
  else if(balance >= 5)
  {
    if(digitalRead(waterPin) == PRESST && !hasWaterTime)
    {
      // clear the display
      lcd.clear();
      balance -= waterCost;
      startWaterMillis = millis();
      isWatering = true;
      hasWaterTime = true;
      
      Serial.println(F("WATER IS RUNNING"));
      Serial.print(F("BALANCE: P"));
      Serial.println(balance);
    }
    
    if(!hasWaterTime)
    {
      DisplayPress();
      DisplayBalance(balance);
    }
  }
  
  PumpWater();
  if(digitalRead(waterPin) == PRESST && hasWaterTime)
  {
    TogglePumpWater();
  }
}

int AddCoinTo(int bal)
{
  if(digitalRead(coinPin_1) == PRESST)
  {
    bal += 1;
    Serial.print(F("BALANCE: P"));
    Serial.println(bal);
  }
  if(digitalRead(coinPin_5) == PRESST)
  {
    bal += 5;
    Serial.print(F("BALANCE: P"));
    Serial.println(bal);
  }
  if(digitalRead(coinPin_10) == PRESST)
  {
    bal += 10;
    Serial.print(F("BALANCE: P"));
    Serial.println(bal);
  }
  return bal;
}

void DisplayBalance(int bal)
{
  lcd.setCursor(0, 1);
  lcd.print("  BALANCE: P");
  lcd.print(bal);
}

void DisplayInsert()
{
  lcd.home();
  lcd.print("INSERT:P1,P5,P10");
}

void DisplayPress()
{
  lcd.home();
  lcd.print(" PRESS A BUTTON ");
}

void PumpWater()
{
  currentWaterMillis = millis();
  long displayWaterTime;
  if(isWatering)
  {
    unsigned long elapsedWaterTime = currentWaterMillis - startWaterMillis;
    if(elapsedWaterTime > waterTimeLeft)
    { 
      if(digitalRead(relayPin) == 1)
      {
        digitalWrite(relayPin, 0);
      }
      displayWaterTime = 0;
      
      isWatering = false;
      hasWaterTime = false;
    }
    else if(elapsedWaterTime <= waterTimeLeft)
    {
      if(digitalRead(relayPin) == 0)
      {
        digitalWrite(relayPin, 1);
      }
      displayWaterTime = waterTimeLeft - elapsedWaterTime;      
    }
  }
  else if(!isWatering)
  {
    if(digitalRead(relayPin) == 1)
    {
      digitalWrite(relayPin, 0);
    }
    if(hasWaterTime)
    {
      displayWaterTime = waterTimeLeft;
    }
  }
  
  // Display Time
  if(hasWaterTime)
  {
    int countdown_minute = ((displayWaterTime / 1000) / 60) % 60;
    int countdown_sec = (displayWaterTime / 1000) % 60;
    
    lcd.home();
    lcd.print("WTR");
    if(countdown_minute < 10)
    {
      lcd.print(" ");
    }
    lcd.print(countdown_minute);
    lcd.print(":");
    if(countdown_sec < 10)
    {
      lcd.print("0");
    }
    lcd.print(countdown_sec);
  }
  // Reset the waterTimeLeft after the countdown is finished
  if(displayWaterTime == 0)
  {
    waterTimeLeft = waterTime;
  }
}

// Play/Pause toggle of the water time countdown:
void TogglePumpWater()
{
  isWatering = !isWatering;
  if(isWatering)
  {
    startWaterMillis = millis();
    Serial.println(F("toggled: WATER IS RUNNING"));
  }
  else if(!isWatering)
  {
    waterTimeLeft -= (millis() - startWaterMillis);
    Serial.println(F("toggled: WATER HAS STOPPED"));
  }
}
COIN 1
WATER
FOAM
RELAY
COIN 5
COIN 10