#include <MD_MAX72xx.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "RTClib.h"

// WiFi credentials
const char* ssid = "yourSSID";
const char* password = "yourPASSWORD";



#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels


// Define MAX7219 parameters
#define MAX_DEVICES 4
#define CLK_PIN   19
#define DATA_PIN  23
#define CS_PIN    18

// Specify the correct module type for your MAX7219 module
#define MODULE_TYPE MD_MAX72XX::PAROLA_HW

// Create a new instance of the MD_MAX72XX class
MD_MAX72XX mx = MD_MAX72XX(MODULE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// RTC object
RTC_DS1307 RTC;
char days[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


// Sensor pins
const int photoelectricPin = 32;
const int laserInterruptPin = 33;
const int resetCountPin= 2;

//Output pins
const int mechCounterPin =13;
const int buzzerPin=12;

// Object counter
int objectCount = 0;
DateTime now;

void setup() {
  Serial.begin(115200);
  Serial.println("Hello");
  
 /* // Start WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print("hello.");
  }
  Serial.println("\nWiFi connected.");
*/
  // Initialize RTC
   if (!RTC.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  RTC.adjust(DateTime(F(__DATE__), F(__TIME__)));

  //Initialize OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  delay(1000);
  display.clearDisplay();

  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(30, 20);
  // Display static text;
  display.println("RTC");
  display.display();
  //delay(3000);
  display.clearDisplay();



  // Initialize LED Matrix
  mx.begin();
  mx.control(MD_MAX72XX::TEST, true);
  delay(200);
  mx.control(MD_MAX72XX::TEST, false);
  mx.control(MD_MAX72XX::INTENSITY, 5);


  // Initialize pins
  pinMode(photoelectricPin, INPUT_PULLUP);
  pinMode(laserInterruptPin, INPUT_PULLUP);
  pinMode(mechCounterPin, OUTPUT);
  pinMode(resetCountPin, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);
}
int pflag=0;// is the flag to say photoelectric sensor is armed
int stuckflag=0; //this is the stuck flag when both sensors are on
int laserflag=0; // this is  the laser trigger flag
void loop() {
  //checkstuck();
    if (digitalRead(resetCountPin)== LOW) {    
    objectCount=0;   // reset count
     updateLEDMatrix(); // Update LED matrix display
        sendToOled(); //update OLED
  }
  if ((digitalRead(photoelectricPin) == LOW) && pflag==0 && stuckflag==0) {
    delay(50); // Debounce delay
    pflag=1; // is the flag to say photoelectric sensor is triggered
  }
   // if (digitalRead(photoelectricPin) == LOW) {
      if ((digitalRead(laserInterruptPin) == LOW) && pflag==1 && stuckflag==0 && laserflag==0) {
        laserflag=1;
      }
      if (pflag ==1 && laserflag ==1 && stuckflag==0){
        objectCount++;
        now = RTC.now(); // Get current time
        updateLEDMatrix(); // Update LED matrix display
        sendToOled(); //update OLED
        sendToCounter(); // pulse the mechanical counter 
      // sendDataToServer();
        delay(100); // Adjust for object passing time 
         pflag=0; //reset the photoelectric sensor flag is armed 
         laserflag=0; //reset the laser sensor flag
      }
checkstuck();
  sendToOled();
}

void checkstuck() {
  if ((digitalRead(laserInterruptPin) == LOW) && (digitalRead(photoelectricPin) == LOW) ) {
  stuckflag=1;   //both sensors are stuck then arm the stuck flag bit
  soundBuzzer();
}
if ((digitalRead(laserInterruptPin) == HIGH) ||   (digitalRead(photoelectricPin) == HIGH) ) {
  stuckflag=0;   //reset the stuck flag bit
   }
   
}

void updateLEDMatrix() {
    mx.clear();
    mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); // Turn off automatic updates

    char buf[10];
    snprintf(buf, sizeof(buf), "%4d", objectCount);
    int bufLen = strlen(buf);

    // Calculate starting position for right-aligned text and shift left by 4 pixels
    int startPos = MAX_DEVICES * 8 - bufLen * 8 + 4;

    for (int i = 0; i < bufLen; i++) {
        // Place each character starting from the rightmost position
        mx.setChar(startPos + i * 8, buf[bufLen - 1 - i]);
    }

    //Serial.print(objectCount);
    Serial.println(objectCount);
   // Serial.print(pflag);

    mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); // Turn on automatic updates
    mx.update();
}


void sendDataToServer() {
  // Send data to server every 10 seconds
  static unsigned long lastTime = 0;
  if ((WiFi.status() == WL_CONNECTED) && (millis() - lastTime > 10000)) {
    lastTime = millis();

    WiFiClient client;
    HTTPClient http;

    http.begin(client, "http://example.com/update"); // Replace with your server URL
    http.addHeader("Content-Type", "application/json");

    char timeStr[20];
    snprintf(timeStr, 20, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    String httpRequestData = "{\"count\":" + String(objectCount) + ",\"time\":\"" + timeStr + "\"}";
    
    int httpResponseCode = http.POST(httpRequestData);

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println(httpResponseCode);
      Serial.println(response);
    } else {
      Serial.print("Error on sending POST: ");
      Serial.println(httpResponseCode);
    }

    http.end();
  }
}

void sendToOled(){
  DateTime now = RTC.now();

  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(now.day());
  display.print('/');
  display.print(now.month());
  display.print('/');
  display.print(now.year());
  display.print('-');
  display.println(days[now.dayOfTheWeek()]);
  display.println(' ');
  display.setCursor(15, 14);
  display.setTextSize(2);
  if (now.hour() < 10)
    display.print('0');
  display.print(now.hour());
  display.print(':');
  if (now.minute() < 10)
    display.print('0');
  display.print(now.minute());
  display.print(':');
  if (now.second() < 10)
    display.print('0');
  display.println(now.second());
   display.setCursor(5, 34);
  display.setTextSize(4);
  display.print(objectCount);
  display.drawLine(1,33,127,33, SSD1306_WHITE);
  display.drawLine(1,33,1,63, SSD1306_WHITE);
  display.drawLine(127,33,127,63, SSD1306_WHITE);
  display.drawLine(1,63,127,63, SSD1306_WHITE);
  display.display();
}

void sendToCounter() {
  digitalWrite(mechCounterPin, HIGH);
  delay(100);
  digitalWrite(mechCounterPin, LOW);
}

void soundBuzzer() {
  digitalWrite(buzzerPin, HIGH); //Keep buzzer off
  delay(200);
  digitalWrite(buzzerPin, LOW); //Keep buzzer on
  delay(50);
}
GND5VSDASCLSQWRTCDS1307+
NOCOMNCVCCGNDINLED1PWRRelay Module