#include "tone.h";

// Miscallaneous inputs/outputs
#define UNARMED_LED 2       // LED to light up when unarmed
#define ARMED_LED 13        // LED to light up when armed
#define ARM_UNARM_BUTTON 3  // button used to arm/unarm
#define SPKR 12

// Security points
const int door1 = 4;
const int door2 = 5;

// Configuration parameters
const String pass        = "113";  // password
const int securityPoints = 2;      // number of security points

const int maxWarnLevel  = 250;  // maximum warnLevel
const int warnDelayRef  = 300;  // the number to subtract warnLevel from
const int warnIncrement = 7;    // increment maxLevel by this number

// Program booleans
bool isArmed    = 1;  // armed status. set to initialize as armed or unarmed
bool soundAlarm = 0;  // should the program sound the alarm?
bool unarmButtonDown = 0;  // boolean that prevents unarm right after rearm
                           // until unarm button is depressed

// Program variables
int warnLevel              = 0;
int isOpen[securityPoints] = {  // status of security points.
    false, false};

bool waitingForPass = 0;
String readString;

unsigned long currentMillis   = millis();
unsigned long unarmIntTracker = 0;
unsigned long alarmIntTracker = 0;
bool flipflopUnarmState       = LOW;
bool flipflopAlarmState       = LOW;

// Melodies
int initMelody[]    = {FS4, GS4, AS4, CS5, DS5, FS5};
int initDurations[] = {100, 100, 100, 100, 100, 100};

int unarmMelody[]    = {B3, B2};
int unarmDurations[] = {50, 50};

int armMelody[]    = {C5, C6};
int armDurations[] = {50, 50};

int alarmNote = CS5;

int noteDelay = 40;

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

	// Security points
	pinMode(door1, INPUT_PULLUP);
	pinMode(door2, INPUT_PULLUP);

	// Misc. inputs/outputs
	pinMode(ARM_UNARM_BUTTON, INPUT_PULLUP);
	pinMode(UNARMED_LED, OUTPUT);
	pinMode(ARMED_LED, OUTPUT);

	Serial.println("Initialized.");

	playMelody(6, initMelody, initDurations, noteDelay);

	if (isArmed == 1) {
		Serial.println("Currently ARMED.");
		digitalWrite(ARMED_LED, HIGH);
	} else {
		Serial.println("Currently UNARMED.");
		digitalWrite(UNARMED_LED, HIGH);
	}
}

// Play an array of tones.
void playMelody(int melodyLength, int *melody, int *noteDur, int noteDelay) {
	for (int i = 0; i < melodyLength; i++) {
		tone(SPKR, melody[i], noteDur[i]);
		delay(noteDelay);
		noTone(SPKR);
	}
}

// Switches an LED's power on/off whenever called.
void flipLed(int ledPin, bool &state) {
	if (state == LOW) {
		state = HIGH;
	} else {
		state = LOW;
	};

	digitalWrite(ledPin, state);
}

void loop() {
	// - Security Points -

	// door1
	if (digitalRead(door1) == LOW && isOpen[0] == false) {
		Serial.println("Door1 has opened.");
		isOpen[0] = true;
		if (isArmed == true) soundAlarm = 1;
	} else if (digitalRead(door1) == HIGH && isOpen[0] == true) {
		isOpen[0]  = false;
		soundAlarm = 0;
		Serial.println("Door1 has closed.");
	}

	// door2
	if (digitalRead(door2) == LOW && isOpen[1] == false) {
		Serial.println("Door2 has opened.");
		isOpen[1] = true;
		if (isArmed == true) soundAlarm = 1;
	} else if (digitalRead(door2) == HIGH && isOpen[1] == true) {
		isOpen[1]  = false;
		soundAlarm = 0;
		Serial.println("Door2 has closed.");
	}

	// - Arm/Unarm -

	if ((digitalRead(ARM_UNARM_BUTTON) == LOW || waitingForPass == 1) &&
	    unarmButtonDown == 0) {
		if (isArmed == 1 || waitingForPass == 1) {
			if (waitingForPass == 0) {
				waitingForPass = 1;
				Serial.println("To unarm, please enter the password.");
				Serial.print("password> ");
			}

			if (Serial.available()) {
				char c = Serial.read();

				if (c != '\n') {
					readString += c;
				} else {
					Serial.println(readString);

					if (readString == pass) {
						isArmed = 0;

						playMelody(2, unarmMelody, unarmDurations, noteDelay);
						Serial.println("Successfully UNARMED.");
					} else {
						playMelody(2, armMelody, unarmDurations, noteDelay);
						Serial.println("Wrong password!");
					}
					readString     = "";
					waitingForPass = 0;
				}
			}

			currentMillis = millis();

			// async blink unarm led every 250ms
			if (currentMillis - unarmIntTracker >= 1000 && waitingForPass == 1) {
				unarmIntTracker = currentMillis;
				flipLed(UNARMED_LED, flipflopUnarmState);
			}
		} else {
			isArmed         = 1;
			waitingForPass  = 0;
			unarmButtonDown = 1;

			playMelody(2, armMelody, armDurations, noteDelay);
			Serial.println("Successfully ARMED.");
		}
	}

	// - Button safety -
	if (digitalRead(ARM_UNARM_BUTTON) == HIGH && unarmButtonDown == 1) {
		unarmButtonDown = 0;
	}

	// - Ignore Serial input unless we're waiting for it -
	if (waitingForPass == 0) {
		while (Serial.available()) { Serial.read(); }
	}

	// - Activate unarmed/armed LEDs -

	if (isArmed == 1 && waitingForPass == 0 && soundAlarm == 0) {
		digitalWrite(ARMED_LED, HIGH);
		digitalWrite(UNARMED_LED, LOW);
	} else if (waitingForPass == 0 && soundAlarm == 0) {
		digitalWrite(UNARMED_LED, HIGH);
		digitalWrite(ARMED_LED, LOW);
	}

	// - Sound alarm -

	if (soundAlarm == 1) {
		currentMillis = millis();

		if ((currentMillis - alarmIntTracker) >= (warnDelayRef - warnLevel)) {
			alarmIntTracker = currentMillis;

			if (warnLevel < maxWarnLevel) { warnLevel = warnLevel + warnIncrement; };

			flipLed(ARMED_LED, flipflopAlarmState);

			if (flipflopAlarmState == HIGH) {
				tone(SPKR, alarmNote);
			} else {
				noTone(SPKR);
			}
		}
	} else {
		warnLevel = 0;
		noTone(SPKR);
	}
}