/**
*****************************************************************************
**
**  File        : main.c
**
**  Abstract    : main function.
**
**  Functions   : main
**
**  Environment : Atollic TrueSTUDIO/STM32
**                STMicroelectronics STM32F10x Standard Peripherals Library
**
**  Distribution: The file is distributed “as is,” without any warranty
**                of any kind.
**
**  (c)Copyright Atollic AB.
**  You may use this file as-is or modify it according to the needs of your
**  project. Distribution of this file (unmodified or modified) is not
**  permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
**  rights to distribute the assembled, compiled & linked contents of this
**  file as part of an application binary file, provided that it is built
**  using the Atollic TrueSTUDIO(R) toolchain.
**
**
*****************************************************************************
*/

/* Includes */
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "drivers/drivers.h"

#include "ServicePort/setting.h"
#include "uIP/app/uip-app.h"
#include "uIP/app/rfid/rfid.h"
#include "timer.h"

#include "aes/aes.h"


enum {
	State_NOTCONNECTED,
	State_IDLE,
	State_PIN,
	State_SENDCARD,
	State_RECEIVECARD,
	State_RECEIVEPIN,
	State_NORESPOND,
	State_DENIED,
	State_OPEN,
	State_CHKDOOR,
	State_CHKSENSOR
} mainState = State_NOTCONNECTED;

uint8_t cardId[] = {0, 0, 0, 0, 0};
uint8_t pin[4];
int8_t pinOK = 0;


const unsigned char aes_key[16] =
{
	0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
	0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed, 0x0f
};

aes_context aes_ctx;

//-----------------------------------------------------------------------------



/*
 * Read ID of Mifare card
 */
uint8_t readCard(void) {
	uint32_t n;
	uint8_t zero = 1;
	uint8_t crc = 0;

	memset(cardId, 0, sizeof(cardId));

	for (n = 0; n < 20; n++) {
		if (Mifare_Request()) {							// Try if card is in field
			for (n = 0; n < 20; n++) {					// Try to read 10 times
				if (Mifare_AntiCollision(cardId)) {		// Get card ID

					for (n = 0; n < 4; n++) {
						crc ^= cardId[n];				// Compute CRC
						if (cardId[n] != 0) zero = 0;	// Zero number control
					}

					if (zero) return 0;

					if (cardId[4] == crc) {				// Check CRC
						Piezo_Tone(Tone_C4);			// Beep
						n = 100000;
						while(n--);
						Piezo_Tone(Tone_None);
						return 1;
					}
					return 0;
				}
			}
		}
	}

	return 0;
}

//-----------------------------------------------------------------------------
uint8_t pin_position;

/*
 * Prepare to reading PIN
 */

void preparePIN(void) {
	pin_position = 0;
	pinOK = 0;

	LCD_Clear();
	LCD_GotoXY(0, 1);
	LCD_PutS("Enter PIN: ____");
	timerSet(5);
	mainState = State_PIN;
}

/*
 * Read PIN from keyboard
 */
uint8_t readPIN(void) {
	static char lastPressed = 0;
	char c = Key_GetC();
	uint32_t i;

	LCD_GotoXY(11 + pin_position, 1);
	if (c) {
		timerSet(10);

		if (lastPressed) return 0;
		lastPressed = 1;

		pin[pin_position] = c;
		LCD_PutC('*');

		Piezo_Tone(Tone_C2);							// Beep
		i = 100000;
		while(i--);
		Piezo_Tone(Tone_None);

		pin_position ++;
		if (pin_position == 4) return 1;
	}
	else {
		lastPressed = 0;
	}
	return 0;
}

//-----------------------------------------------------------------------------

void prepareIdle(void) {
	mainState = State_IDLE;

	Mfrc530_DriverOn();

	LCD_Clear();
	LCD_GotoXY(0, 1);
	LCD_PutS("Place card");
	timerSet(setting.doorTimeout);
}

void prepareSendCard(void) {
	mainState = State_SENDCARD;
}

void prepareReceiveCard(void) {
	mainState = State_RECEIVECARD;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("Verifying card");
}

void prepareReceivePIN(void) {
	mainState = State_RECEIVEPIN;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("Verifying PIN");
}

void prepareNoRespond(void) {
	mainState = State_NORESPOND;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("No respond");
	timerSet(2);
}

void prepareDenied(char *message) {
	mainState = State_DENIED;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS(message);
	timerSet(5);
}

void prepareOpen(void) {
	mainState = State_OPEN;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("Opened...");
	timerSet(5);
}

uint8_t getDoorControlState(void) {
	if (mainState == State_CHKDOOR) return 0;
	else return 1;
}

void prepareCheckDoor(void) {
	mainState = State_CHKDOOR;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("Door opened!");
	Piezo_Tone(Tone_C3);
}

void prepareCheckSensors(void) {
	mainState = State_CHKSENSOR;
	LCD_Clear();
	LCD_GotoXY(0,1);
	LCD_PutS("Sensor problem!");
}

void show_status(void) {
	char str[17];

	if (!uip_app_linked() && mainState != State_NOTCONNECTED) {
		mainState = State_NOTCONNECTED;
		LCD_Clear();
		LCD_GotoXY(0,1);
		LCD_PutS("Disconnected");
	}

	LCD_GotoXY(0, 0);
	LCD_PutS(setting.devName);


	sprintf(str, "%02d:%02d", RTC_GetHour(),
							  RTC_GetMinute());

	LCD_GotoXY(11, 0);
	LCD_PutS(str);
}

//-----------------------------------------------------------------------------

int main(void) {
	Drivers_Init();

	Setting_Load();
	uip_app_init();

	while (!uip_app_linked());
	LCD_Clear();

	while (1) {
		uip_app_loop();
		show_status();

		switch (mainState) {
		case State_NOTCONNECTED:
			if (uip_app_linked()) prepareIdle();
			break;
		case State_IDLE:
			if (DoorLock_GetDoorLoop()) timerSet(setting.doorTimeout);
			if (timerExpired() && setting.doorTimeout != 0) {
				Mfrc530_DriverOff();
				prepareCheckDoor();
			}
			if (!DoorLock_GetLockLoop() ||
				ADC1_GetValueFilt(SENSOR1) < setting.calibCh1 ||
				ADC1_GetValueFilt(SENSOR2) < setting.calibCh2) {

				Mfrc530_DriverOff();
				prepareCheckSensors();
			}
			if (readCard()) {
				Mfrc530_DriverOff();
				prepareSendCard();
			}
			break;
		case State_CHKDOOR:
			if (DoorLock_GetDoorLoop()) {
				Piezo_Tone(Tone_None);
				prepareIdle();
			}
			break;
		case State_CHKSENSOR:
			if (DoorLock_GetLockLoop() &&
				ADC1_GetValueFilt(SENSOR1) >= setting.calibCh1 &&
				ADC1_GetValueFilt(SENSOR2) >= setting.calibCh2) {

				prepareIdle();
			}
			break;
		case State_PIN:
			if (!timerExpired()) {
				if (readPIN()) {
					pinOK = 1;
					prepareReceivePIN();
				}
			}
			else {
				pinOK = -1;
				prepareIdle();
			}
			break;
		case State_SENDCARD:
			uip_rfid_request();
			prepareReceiveCard();
			break;
		case State_RECEIVECARD:
		case State_RECEIVEPIN:
			break;
		case State_NORESPOND:
		case State_DENIED:
			if (timerExpired()) prepareIdle();
			break;
		case State_OPEN:
			if (timerExpired()) {
				DoorLock_Close();
				prepareIdle();
			}
			break;
		default:
			prepareIdle();
		}
	}
}
