#include <ZUNO_SERVO.h>
#include <EEPROM.h>

#define CONSOLE_SERIAL Serial
#define RFID_SERIAL Serial1

#define SERVO_PIN 12
const int SERVO_ENABLE_PIN = 9;

const int SENSOR_LOCK_PIN = 2;
const int SENSOR_HANDLE_PIN = 1;
const int SENSOR_DISABLE_PIN = 0;
const int SENSOR_CLOSE_PIN = 10;

const int BTN_LOCK_PIN = 3;
const int BTN_UNLOCK_PIN = 4;
const int LED_LOCK_PIN = 5;
const int LED_UNLOCK_PIN = 6;

#define SERVO_DISABLE 0
#define SERVO_LOCK 1
#define SERVO_UNLOCK 2
#define SERVO_CENTER 3

#define SOURCE_EXTERNAL 0
#define SOURCE_RFID 1
#define SOURCE_ZWAVE 2
#define SOURCE_BUTTONS 3
#define SOURCE_SYSTEM 4

#define SERVO_TRIM 2

// Last saved LED value
byte currentLEDValue = 0;
unsigned long int rfid = 0;

#define SIGNAL_DEBOUNCE_CONSTANT 30

bool sensor_door_locked = false;
bool sensor_handle_in_up_position = false;
bool sensor_not_home_activated = false;
bool sensor_door_closed = false;

bool sensor_door_locked_changed = false;
bool sensor_door_closed_changed = false;
bool sensor_not_home_activated_changed = false;

int sensor_door_locked_prev_state = -1;
int sensor_door_locked_current_state = LOW;
int sensor_door_locked_debounce = 0;
int sensor_handle_in_up_position_prev_state = -1;
int sensor_handle_in_up_position_current_state = LOW;
int sensor_handle_in_up_position_debounce = 0;
int sensor_not_home_activated_prev_state = -1;
int sensor_not_home_activated_current_state = LOW;
int sensor_not_home_activated_debounce = 0;
int sensor_door_closed_prev_state = -1;
int sensor_door_closed_current_state = LOW;
int sensor_door_closed_debounce = 0;

bool btn_lock = false;
bool btn_unlock = false;

int btn_lock_prev_state = -1;
int btn_lock_current_state = LOW;
int btn_lock_debounce = 0;
int btn_unlock_prev_state = -1;
int btn_unlock_current_state = LOW;
int btn_unlock_debounce = 0;

int activity_flash = 0;
int servo_status = 0;
int source_status = 0;

unsigned long current_timestamp = 0;
unsigned long activity_timestamp = 0;
unsigned long servo_timestamp = 0;
unsigned long rfid_timeout_timestamp = 0;

bool lock_disable = false;

#define RFID_PACKAGE_LENGHT 14 // nmbr of bytes in package
#define RFID_START_BYTE 0x02   // start byte
#define RFID_STOP_BYTE 0x03    // stop byte
#define RFID_TAG_BYTES 10 // number of bytes in package that represent the tag
#define RFID_TAGS 10       // Number of tags to be supported to store in EEPROM
#define EEPROM_CURRENT_TAG_ADDRESS 0 // Addres for storing current address in the tag memory
#define EEPROM_TAG_START_ADDRESS 1 // Start addres for storing RFID tags
int rfid_current_checksum = 0;
int rfid_package[RFID_PACKAGE_LENGHT];
int rfid_package_pos = 0;
int rfid_tags[RFID_TAGS][RFID_TAG_BYTES];
int rfid_store_tag_nbr = 0;
bool rfid_store_tag = false;

ServoController servo(12);

ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_ALWAYS_AWAKE);

// set up channel
ZUNO_SETUP_CHANNELS(
	ZUNO_SWITCH_BINARY(getterLock, setterLock),
	ZUNO_SWITCH_BINARY(getterLockDisable, setterLockDisable),
	ZUNO_SWITCH_MULTILEVEL(getterStoreRFID, setterStoreRFID),
	ZUNO_SENSOR_BINARY_DOOR_WINDOW(getterDoor)
);

void load_rfid_keys() {
	int address = EEPROM_TAG_START_ADDRESS;
	for (int i = 0; i < RFID_TAGS; i++) {
		for (int j = 0; j < RFID_TAG_BYTES; j++) {
			rfid_tags[i][j] = EEPROM.read(address);
			address++;
		}
	}
	CONSOLE_SERIAL.println("RFID tags loaded from EEPROM");
}

void update_rfid() {
	int rfid_read = 0;
	if (RFID_SERIAL.available()) {
		while (RFID_SERIAL.available() > 0) {
			rfid_read = RFID_SERIAL.read();

			// Check for package start
			if (rfid_read == RFID_START_BYTE) {
				rfid_package_pos = 0;
			}
			// Check for package end
			if (rfid_read == RFID_STOP_BYTE) {
				if (rfid_store_tag) {
					int rfid_eeprom_pos = rfid_store_tag_nbr-1 * RFID_TAG_BYTES;
					for (int i = 0; i < RFID_TAG_BYTES; i++) {
						EEPROM.write(rfid_eeprom_pos, rfid_package[i + 1]);
						rfid_tags[rfid_store_tag_nbr-1][i] = rfid_package[i + 1];
						rfid_eeprom_pos++;
					}

					rfid_store_tag = false;	
					rfid_store_tag_nbr = 0;	
					zunoSendReport(3);
					
				} else {
					// Check for match of RFID tag
					for (int i = 0; i < RFID_TAGS; i++) {
						bool tag_ok = true;
						for (int j = 0; j < RFID_TAG_BYTES; j++) {
							if (rfid_package[j + 1] != rfid_tags[i][j]) {
								tag_ok = false;
							}
						}
						if (tag_ok == true) {
							CONSOLE_SERIAL.println("RFID tag verified");
							if (sensor_door_locked){
								set_servo(SERVO_UNLOCK, SOURCE_RFID);
							}
							else{
								set_servo(SERVO_LOCK, SOURCE_RFID);
							}
							break;
						}
					}
				}
			} else {
				rfid_package[rfid_package_pos] = rfid_read;
			}
			if (rfid_package_pos < RFID_PACKAGE_LENGHT - 1) {
				rfid_package_pos++;
			}
		}
	}
}

void set_servo(int mode, int source){
	bool ok_to_lock_unlock = false;

	// Check sensor status if ok to lock/unlock	
	if (sensor_handle_in_up_position == true && sensor_not_home_activated == false && sensor_door_closed == true && lock_disable == false){
		ok_to_lock_unlock = true;
	}

	// Set servo position
	if (mode == SERVO_LOCK && ok_to_lock_unlock && sensor_door_locked == false){
		servo.setValue(160+SERVO_TRIM);
		servo_status = mode;
		source_status = source;
	}
	else if (mode == SERVO_UNLOCK && ok_to_lock_unlock && sensor_door_locked == true){
		servo.setValue(20+SERVO_TRIM);
		servo_status = mode;
		source_status = source;
	}
	else if (mode == SERVO_CENTER){
		servo.setValue(90+SERVO_TRIM);
		servo_status = mode;
	}
	else if (mode == SERVO_DISABLE){
		digitalWrite(SERVO_ENABLE_PIN, LOW);
		servo_status = mode;
	}
	
	// Start servo	
	if (mode > SERVO_DISABLE){
		servo.begin();
		digitalWrite(SERVO_ENABLE_PIN, HIGH);
		servo_timestamp = current_timestamp + 700;
	}
}

void update_buttons(){
	sensor_door_locked_current_state = digitalRead(SENSOR_LOCK_PIN);
	sensor_handle_in_up_position_current_state = digitalRead(SENSOR_HANDLE_PIN);
	sensor_not_home_activated_current_state = digitalRead(SENSOR_DISABLE_PIN);
	sensor_door_closed_current_state = digitalRead(SENSOR_CLOSE_PIN);
	
	btn_lock_current_state = digitalRead(BTN_LOCK_PIN);
	btn_unlock_current_state = digitalRead(BTN_UNLOCK_PIN);
	
	// Debounce
	if (sensor_door_locked_current_state != sensor_door_locked_prev_state) {
		if (++sensor_door_locked_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (sensor_door_locked_current_state == LOW) {
				sensor_door_locked = false;
			}
			else{
				sensor_door_locked = true;
			}
			sensor_door_locked_prev_state = sensor_door_locked_current_state;
			sensor_door_locked_debounce = 0;
			sensor_door_locked_changed = true;
		}
	} else {
		sensor_door_locked_debounce = 0;
	}
	if (sensor_handle_in_up_position_current_state != sensor_handle_in_up_position_prev_state) {
		if (++sensor_handle_in_up_position_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (sensor_handle_in_up_position_current_state == LOW) {
				sensor_handle_in_up_position = true;
			}
			else{
				sensor_handle_in_up_position = false;
			}
			sensor_handle_in_up_position_prev_state = sensor_handle_in_up_position_current_state;
			sensor_handle_in_up_position_debounce = 0;
		}
	} else {
		sensor_handle_in_up_position_debounce = 0;
	}
	if (sensor_not_home_activated_current_state != sensor_not_home_activated_prev_state) {
		if (++sensor_not_home_activated_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (sensor_not_home_activated_current_state == LOW) {
				sensor_not_home_activated = false;
			}
			else{
				sensor_not_home_activated = true;
			}
			sensor_not_home_activated_prev_state = sensor_not_home_activated_current_state;
			sensor_not_home_activated_debounce = 0;
			sensor_not_home_activated_changed = true;
		}
	} else {
		sensor_not_home_activated_debounce = 0;
	}
	if (sensor_door_closed_current_state != sensor_door_closed_prev_state) {
		if (++sensor_door_closed_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (sensor_door_closed_current_state == LOW) {
				sensor_door_closed = true;
			}
			else{
				sensor_door_closed = false;
			}
			sensor_door_closed_prev_state = sensor_door_closed_current_state;
			sensor_door_closed_debounce = 0;
			sensor_door_closed_changed = true;
		}
	} else {
		sensor_door_closed_debounce = 0;
	}
	if (btn_lock_current_state != btn_lock_prev_state) {
		if (++btn_lock_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (btn_lock_current_state == LOW) {
				if (servo_status == SERVO_DISABLE){
					btn_lock = true;
				}
			}
			btn_lock_prev_state = btn_lock_current_state;
			btn_lock_debounce = 0;
		}
	} else {
		btn_lock_debounce = 0;
	}
	if (btn_unlock_current_state != btn_unlock_prev_state) {
		if (++btn_unlock_debounce == SIGNAL_DEBOUNCE_CONSTANT) {
			if (btn_unlock_current_state == LOW) {
				if (servo_status == SERVO_DISABLE){
					btn_unlock = true;
				}
			}
			btn_unlock_prev_state = btn_unlock_current_state;
			btn_unlock_debounce = 0;
		}
	} else {
		btn_unlock_debounce = 0;
	}
}

// the setup routine runs once when you press reset:
void setup() {

	pinMode(SENSOR_LOCK_PIN, INPUT_PULLUP);
	pinMode(SENSOR_HANDLE_PIN, INPUT_PULLUP);
	pinMode(SENSOR_DISABLE_PIN, INPUT_PULLUP);
	pinMode(SENSOR_CLOSE_PIN, INPUT_PULLUP);
	pinMode(BTN_LOCK_PIN, INPUT_PULLUP);
	pinMode(BTN_UNLOCK_PIN, INPUT_PULLUP);

  	pinMode(LED_UNLOCK_PIN, OUTPUT); // setup pin as output
	digitalWrite(LED_UNLOCK_PIN, LOW);
  	pinMode(LED_LOCK_PIN, OUTPUT); // setup pin as output
	digitalWrite(LED_LOCK_PIN, LOW);
  	
	pinMode(SERVO_ENABLE_PIN, OUTPUT); // setup pin as output
	digitalWrite(SERVO_ENABLE_PIN, LOW);

	CONSOLE_SERIAL.begin(9600);
	RFID_SERIAL.begin(9600);
	
	load_rfid_keys();
	set_servo(SERVO_CENTER, SOURCE_SYSTEM);	
}

// the loop routine runs over and over again forever:
void loop() {
	current_timestamp = millis();

	// Update all buttons and sensors	
	update_buttons();
	update_rfid();

	// Check button status
	if (btn_lock == true){
		if (rfid_store_tag_nbr > 0 && rfid_store_tag == false){
			int rfid_eeprom_pos = rfid_store_tag_nbr-1 * RFID_TAG_BYTES;
			for (int i = 0; i < RFID_TAG_BYTES; i++) {
				EEPROM.write(rfid_eeprom_pos,0xFF);
				rfid_eeprom_pos++;
			}
			rfid_store_tag_nbr = 0;
			zunoSendReport(3);
		}
		else{
			set_servo(SERVO_LOCK, SOURCE_BUTTONS);
			btn_lock = false;			
			btn_unlock = false;			
		}
	}
	else if (btn_unlock == true){
		if (rfid_store_tag_nbr > 0 && rfid_store_tag == false){
			rfid_store_tag = true;
		}
		else{
			set_servo(SERVO_UNLOCK, SOURCE_BUTTONS);
			btn_unlock = false;			
		}
	}

	// Check if sensor changed and report to Z-Wave
	if (sensor_door_locked_changed){
		sensor_door_locked_changed = false;
		zunoSendReport(1);
	}

	// Check if sensor changed and report to Z-Wave
	if (sensor_not_home_activated_changed){
		sensor_not_home_activated_changed = false;
		zunoSendReport(2);
	}

	// Check if sensor changed and report to Z-Wave
	if (sensor_door_closed_changed){
		sensor_door_closed_changed = false;
		zunoSendReport(4);
	}

	// Check if servo needs to be centered or turned off
	if (servo_status > SERVO_DISABLE) {
		if (current_timestamp >= servo_timestamp) {
			if (servo_status < SERVO_CENTER) {
				set_servo(SERVO_CENTER, source_status);
			}else{	
				set_servo(SERVO_DISABLE, source_status);
			}
		}
	}

	// Check if store RFID has timed out
	if (rfid_store_tag_nbr > 0) {
		if (current_timestamp >= rfid_timeout_timestamp) {
			rfid_store_tag = false;
			rfid_store_tag_nbr = 0;	
			zunoSendReport(3);
		}
	}

	// Indicate that the door is being locked
	if (servo_status == SERVO_LOCK){
		if (current_timestamp >= activity_timestamp) {
			if (activity_flash == 0){
				digitalWrite(LED_LOCK_PIN, HIGH);
				digitalWrite(LED_UNLOCK_PIN, LOW);
				activity_flash = 1;
			}
			else{
				digitalWrite(LED_LOCK_PIN, LOW);
				digitalWrite(LED_UNLOCK_PIN, LOW);
				activity_flash = 0;
			}
			activity_timestamp = current_timestamp + 50;
		}
	}
	// Indicate that the door is being unlocked
	else if (servo_status == SERVO_UNLOCK){
		if (current_timestamp >= activity_timestamp) {
			if (activity_flash == 0){
				digitalWrite(LED_LOCK_PIN, LOW);
				digitalWrite(LED_UNLOCK_PIN, HIGH);
				activity_flash = 1;
			}
			else{
				digitalWrite(LED_LOCK_PIN, LOW);
				digitalWrite(LED_UNLOCK_PIN, LOW);
				activity_flash = 0;
			}
			activity_timestamp = current_timestamp + 50;
		}
	}
	else if (servo_status == SERVO_DISABLE){
		// Indicate RFID storing mode
		if (rfid_store_tag_nbr > 0){
			digitalWrite(LED_LOCK_PIN, HIGH);
			digitalWrite(LED_UNLOCK_PIN, HIGH);
		}
		// Indicate that the lock/unlock is disabled
		else if (sensor_not_home_activated == true || lock_disable == true){
			if (current_timestamp >= activity_timestamp) {
				if (activity_flash == 0){
					digitalWrite(LED_LOCK_PIN, HIGH);
					digitalWrite(LED_UNLOCK_PIN, LOW);
					activity_flash = 1;
				}
				else{
					digitalWrite(LED_LOCK_PIN, LOW);
					digitalWrite(LED_UNLOCK_PIN, LOW);
					activity_flash = 0;
				}
				activity_timestamp = current_timestamp + 500;
			}
		}
		// Indicate that the door is open
		else if (sensor_door_closed == false || sensor_handle_in_up_position == false){
			if (current_timestamp >= activity_timestamp) {
				if (activity_flash == 0){
					digitalWrite(LED_LOCK_PIN, LOW);
					digitalWrite(LED_UNLOCK_PIN, HIGH);
					activity_flash = 1;
				}
				else{
					digitalWrite(LED_LOCK_PIN, LOW);
					digitalWrite(LED_UNLOCK_PIN, LOW);
					activity_flash = 0;
				}
				activity_timestamp = current_timestamp + 500;
			}
		}
		// Indicate that the door is locked
		else if (sensor_door_locked == true){
			digitalWrite(LED_LOCK_PIN, HIGH);
			digitalWrite(LED_UNLOCK_PIN, LOW);
		}
		// Indicate that the door is unlocked
		else{
			digitalWrite(LED_LOCK_PIN, LOW);
			digitalWrite(LED_UNLOCK_PIN, HIGH);
		}
	}
}

void setterLock(byte value) {
	if (value == 0){
		set_servo(SERVO_UNLOCK, SOURCE_ZWAVE);
	}
	else{
		set_servo(SERVO_LOCK, SOURCE_ZWAVE);
	}
}

byte getterLock(void) {
	return sensor_door_locked;
}

void setterLockDisable(byte value) {
	if (value == 0){
		lock_disable = false;
	}
	else{
		lock_disable = true;
	}
}

byte getterLockDisable(void) {
	return lock_disable;
}

void setterStoreRFID(byte value) {
	if (rfid_store_tag_nbr == 0 && value > 0 && value < RFID_TAGS + 1){
		rfid_store_tag_nbr = value;
		rfid_timeout_timestamp = current_timestamp + 30000;
	}
	else if (value == 0){
		rfid_store_tag_nbr = 0;
		rfid_store_tag = false
	}
}

byte getterStoreRFID(void) {
	return rfid_store_tag_nbr;
}

byte getterDoor(void) {
	return sensor_door_closed;
}

