#include <Arduino.h>
#include <avr/sleep.h>

#include <Wire.h>
#include <RtcDS3231.h>

RtcDS3231<TwoWire> Rtc(Wire);

const int ledPin = 17;
const int wakeUpPin = 7;
const int wakeUpInterrupt = digitalPinToInterrupt(wakeUpPin);

volatile uint16_t interuptCount = 0;
volatile bool interuptFlag = false;

bool Alarmed();
void printDateTime(const RtcDateTime &dt);

void ISR_ATTR InteruptServiceRoutine()
{
    // sleep_disable();
    // detachInterrupt(wakeUpInterrupt);

    interuptCount++;
    interuptFlag = true;
}

void sleepNow()
{
    // set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    noInterrupts();
    // sleep_enable();
    attachInterrupt(wakeUpInterrupt, InteruptServiceRoutine, FALLING);
    interrupts();
    // sleep_cpu();
}

void setup()
{
    pinMode(wakeUpPin, INPUT_PULLUP);
    pinMode(ledPin, OUTPUT);

    Serial.begin(9660);
    Rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);

    if (!Rtc.IsDateTimeValid())
    {
        if (Rtc.LastError() != 0)
        {
            // we have a communications error
            // see https://www.arduino.cc/en/Reference/WireEndTransmission for
            // what the number means
            Serial.print("RTC communications error = ");
            Serial.println(Rtc.LastError());
        }
        else
        {
            Serial.println("RTC lost confidence in the DateTime!");
            Rtc.SetDateTime(compiled);
        }
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    RtcDateTime now = Rtc.GetDateTime();
    if (now < compiled)
    {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        Rtc.SetDateTime(compiled);
    }

    Rtc.Enable32kHzPin(false);
    Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeAlarmOne);

    DS3231AlarmOne alarm1(0, 0, 0, 0, DS3231AlarmOneControl_OncePerSecond);
    Rtc.SetAlarmOne(alarm1);

    Rtc.LatchAlarmsTriggeredFlags();

    attachInterrupt(wakeUpPin, InteruptServiceRoutine, FALLING);
}

// void loop()
// {
//     digitalWrite(ledPin, !digitalRead(ledPin));
//     Serial.println("interrupred");

//     sleepNow();
// }

void loop ()
{
    if (!Rtc.IsDateTimeValid())
    {
        if (Rtc.LastError() != 0)
        {
            // we have a communications error
            // see https://www.arduino.cc/en/Reference/WireEndTransmission for
            // what the number means
            Serial.print("RTC communications error = ");
            Serial.println(Rtc.LastError());
        }
        else
        {
            Serial.println("RTC lost confidence in the DateTime!");
        }
    }

    RtcDateTime now = Rtc.GetDateTime();

    printDateTime(now);
    Serial.println();

    // we only want to show time every 10 seconds
    // but we want to show responce to the interupt firing
    for (int timeCount = 0; timeCount < 20; timeCount++)
    {
        if (Alarmed())
        {
            Serial.print(">>Interupt Count: ");
            Serial.print(interuptCount);
            Serial.println("<<");
            digitalWrite(ledPin, !digitalRead(ledPin));
        }
        delay(500);
    }

    // sleepNow();
}

bool Alarmed()
{
    bool wasAlarmed = false;
    if (interuptFlag)  // check our flag that gets sets in the interupt
    {
        wasAlarmed = true;
        interuptFlag = false; // reset the flag

        // this gives us which alarms triggered and
        // then allows for others to trigger again
        DS3231AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags();

        if (flag & DS3231AlarmFlag_Alarm1)
        {
            Serial.println("alarm one triggered");
        }
        if (flag & DS3231AlarmFlag_Alarm2)
        {
            Serial.println("alarm two triggered");
        }
    }
    return wasAlarmed;
}

#define countof(a) (sizeof(a) / sizeof(a[0]))

void printDateTime(const RtcDateTime& dt)
{
	char datestring[20];

	snprintf_P(datestring,
			countof(datestring),
			PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
			dt.Month(),
			dt.Day(),
			dt.Year(),
			dt.Hour(),
			dt.Minute(),
			dt.Second() );
    Serial.print(datestring);
}

// // CONNECTIONS:
// // DS3231 SDA --> SDA
// // DS3231 SCL --> SCL
// // DS3231 VCC --> 3.3v or 5v
// // DS3231 GND --> GND
// // SQW --->  (Pin19) Don't forget to pullup (4.7k to 10k to VCC)

// /* for software wire use below
// #include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
// #include <RtcDS3231.h>

// SoftwareWire myWire(SDA, SCL);
// RtcDS3231<SoftwareWire> Rtc(myWire);
//  for software wire use above */

// /* for normal hardware wire use below */
// #include <Wire.h> // must be included here so that Arduino library object file references work
// #include <RtcDS3231.h>
// RtcDS3231<TwoWire> Rtc(Wire);
// /* for normal hardware wire use above */

// bool Alarmed();
// void printDateTime(const RtcDateTime& dt);
// const byte LED = 17;

// // Interrupt Pin Lookup Table
// // (copied from Arduino Docs)
// //
// // CAUTION:  The interrupts are Arduino numbers NOT Atmel numbers
// //   and may not match (example, Mega2560 int.4 is actually Atmel Int2)
// //   this is only an issue if you plan to use the lower level interupt features
// //
// // Board           int.0    int.1   int.2   int.3   int.4   int.5
// // ---------------------------------------------------------------
// // Uno, Ethernet    2       3
// // Mega2560         2       3       21      20     [19]      18
// // Leonardo         3       2       0       1       7

// #define RtcSquareWavePin 7 // Mega2560
// #define RtcSquareWaveInterrupt 4 // Mega2560

// // marked volatile so interrupt can safely modify them and
// // other code can safely read and modify them
// volatile uint16_t interuptCount = 0;
// volatile bool interuptFlag = false;

// void ISR_ATTR InteruptServiceRoutine()
// {
//     // since this interupted any other running code,
//     // don't do anything that takes long and especially avoid
//     // any communications calls within this routine
//     interuptCount++;
//     interuptFlag = true;
// }

// void setup ()
// {
//     Serial.begin(9600);

//     // set the interupt pin to input mode
//     pinMode(RtcSquareWavePin, INPUT);

//     //--------RTC SETUP ------------
//     // if you are using ESP-01 then uncomment the line below to reset the pins to
//     // the available pins for SDA, SCL
//     // Wire.begin(0, 2); // due to limited pins, use pin 0 and 2 for SDA, SCL

//     Rtc.Begin();

//     RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);

//     if (!Rtc.IsDateTimeValid())
//     {
//         if (Rtc.LastError() != 0)
//         {
//             // we have a communications error
//             // see https://www.arduino.cc/en/Reference/WireEndTransmission for
//             // what the number means
//             Serial.print("RTC communications error = ");
//             Serial.println(Rtc.LastError());
//         }
//         else
//         {
//             Serial.println("RTC lost confidence in the DateTime!");
//             Rtc.SetDateTime(compiled);
//         }
//     }

//     if (!Rtc.GetIsRunning())
//     {
//         Serial.println("RTC was not actively running, starting now");
//         Rtc.SetIsRunning(true);
//     }

//     RtcDateTime now = Rtc.GetDateTime();
//     if (now < compiled)
//     {
//         Serial.println("RTC is older than compile time!  (Updating DateTime)");
//         Rtc.SetDateTime(compiled);
//     }

//     Rtc.Enable32kHzPin(false);
//     Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeAlarmBoth);

//     // Alarm 1 set to trigger every day when
//     // the hours, minutes, and seconds match
//     RtcDateTime alarmTime = now + 88; // into the future
//     DS3231AlarmOne alarm1(
//             alarmTime.Day(),
//             alarmTime.Hour(),
//             alarmTime.Minute(),
//             alarmTime.Second(),
//             DS3231AlarmOneControl_HoursMinutesSecondsMatch);
//     Rtc.SetAlarmOne(alarm1);

//     // Alarm 2 set to trigger at the top of the minute
//     DS3231AlarmTwo alarm2(
//             0,
//             0,
//             0,
//             DS3231AlarmTwoControl_OncePerMinute);
//     Rtc.SetAlarmTwo(alarm2);

//     // throw away any old alarm state before we ran
//     Rtc.LatchAlarmsTriggeredFlags();

//     // setup external interupt
//     attachInterrupt(RtcSquareWaveInterrupt, InteruptServiceRoutine, FALLING);
// }

// void loop ()
// {
//     if (!Rtc.IsDateTimeValid())
//     {
//         if (Rtc.LastError() != 0)
//         {
//             // we have a communications error
//             // see https://www.arduino.cc/en/Reference/WireEndTransmission for
//             // what the number means
//             Serial.print("RTC communications error = ");
//             Serial.println(Rtc.LastError());
//         }
//         else
//         {
//             Serial.println("RTC lost confidence in the DateTime!");
//         }
//     }

//     RtcDateTime now = Rtc.GetDateTime();

//     printDateTime(now);
//     Serial.println();

//     // we only want to show time every 10 seconds
//     // but we want to show responce to the interupt firing
//     for (int timeCount = 0; timeCount < 20; timeCount++)
//     {
//         if (Alarmed())
//         {
//             Serial.print(">>Interupt Count: ");
//             Serial.print(interuptCount);
//             Serial.println("<<");
//             digitalWrite(LED, !digitalRead(LED));
//         }
//         delay(500);
//     }
// }

// bool Alarmed()
// {
//     bool wasAlarmed = false;
//     if (interuptFlag)  // check our flag that gets sets in the interupt
//     {
//         wasAlarmed = true;
//         interuptFlag = false; // reset the flag

//         // this gives us which alarms triggered and
//         // then allows for others to trigger again
//         DS3231AlarmFlag flag = Rtc.LatchAlarmsTriggeredFlags();

//         if (flag & DS3231AlarmFlag_Alarm1)
//         {
//             Serial.println("alarm one triggered");
//         }
//         if (flag & DS3231AlarmFlag_Alarm2)
//         {
//             Serial.println("alarm two triggered");
//         }
//     }
//     return wasAlarmed;
// }

// #define countof(a) (sizeof(a) / sizeof(a[0]))

// void printDateTime(const RtcDateTime& dt)
// {
// 	char datestring[20];

// 	snprintf_P(datestring,
// 			countof(datestring),
// 			PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
// 			dt.Month(),
// 			dt.Day(),
// 			dt.Year(),
// 			dt.Hour(),
// 			dt.Minute(),
// 			dt.Second() );
//     Serial.print(datestring);
// }

// // #include <Arduino.h>
// // // **** INCLUDES *****
// // #include "LowPower.h"

// // // Use pin 2 as wake up pin
// // const int wakeUpPin = 7;
// // const int ledPin = 17;

// // void wakeUp()
// // {
// //     // Just a handler for the pin interrupt.
// // }

// // void setup()
// // {
// //     // Configure wake up pin as input.
// //     // This will consumes few uA of current.
// //     pinMode(wakeUpPin, INPUT);
// // }

// // void loop()
// // {
// //     // Allow wake up pin to trigger interrupt on low.
// //     attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, LOW);

// //     // Enter power down state with ADC and BOD module disabled.
// //     // Wake up when wake up pin is low.
// //     LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

// //     // Disable external pin interrupt on wake up pin.
// //     detachInterrupt(digitalPinToInterrupt(wakeUpPin));

// //     // Do something here
// //     // Example: Read sensor, data logging, data transmission.
// //     pinMode(ledPin, OUTPUT);
// //     delay(100);
// //     digitalWrite(ledPin, HIGH);
// //     delay(100);
// //     digitalWrite(ledPin, LOW);
// //     delay(100);
// //     pinMode(ledPin, INPUT);
// // }
//