import nfc
import ndef
from nfc.tag import tt1
from nfc.tag import tt2
from nfc.tag import tt3
from nfc.tag import tt4

import array
import binascii
from operator import xor
import csv
import time
from datetime import datetime
from config import Config

# FAVORITE_FILE = "data/favorite-cards.csv"

###################################################################################
#				TAG
###################################################################################


tagtypes = (
	('uid', nfc.tag.tt1.Type1Tag),
	('uid', nfc.tag.tt2.Type2Tag),
	('idm', nfc.tag.tt3.Type3Tag),
	('uid', nfc.tag.tt4.Type4Tag)
)


class TagsReader():

	TAG_READ_ERROR	  = None
	TAG_ERROR		   = -1
	TAG_OK			  = 0
	TAG_OK_ADD		  = 1
	TAG_FAVORITE		= 2
	TAG_SHUTDOWN		= 3
	TAG_UPDATE		  = 4
	TAG_UPLOAD		  = 5
	TAG_CHANGE_ID_NAME  = 6
	TAG_BEAMIAN_ID	  = 7
	TAG_WIFI_CONNECT	= 8
	TAG_WIFI_DISCONNECT = 9
	TAG_REFERENCE	   = 10
	TAG_LORA_CONNECT	= 11
	TAG_LORA_DISCONNECT = 12
	TAG_AUTO_TEST_START = 13
	TAG_AUTO_TEST_STOP  = 14
	TAG_CLEAR_ALL_TAGS  = 15
	TAG_CLEAR_WIFI_TAGS = 16
	TAG_CLEAR_LORA_TAGS = 17
	TAG_KISS			= 18
	TAG_STATUS		  = 19
	TAG_UPLOAD_GLOBAL = 20
	TAG_CLEAR_GLOBAL = 21

	MSG_TAG_OK			  = ''
	MSG_TAG_FAVORITE		= 'Favorite'
	MSG_TAG_SHUTDOWN		= 'Turning Off'
	MSG_TAG_UPDATE		  = 'Updating Firmware'
	MSG_TAG_UPLOAD		  = 'Uploading Log File'
	MSG_TAG_WIFI_CONNECT	= 'Connecting to a WiFi Network'
	MSG_TAG_WIFI_DISCONNECT = 'Disconnecting WiFi'
	MSG_TAG_LORA_CONNECT	= 'Connecting to LoRa Network'
	MSG_TAG_LORA_DISCONNECT = 'Disconnecting LoRa'
	MSG_TAG_AUTO_TEST_START = 'Start automatic test'
	MSG_TAG_AUTO_TEST_STOP  = 'Stop automatic test'
	MSG_TAG_CRC_ERROR	   = 'NTAG CRC not valid'
	MSG_TAG_INVALID_PAYLOAD = 'Invalid payload'
	MSG_TAG_REFERENCE	   = 'Reference'
	MSG_CLEAR_ALL_TAGS	  = 'Clear ALL tags'
	MSG_CLEAR_WIFI_TAGS	 = 'Clear WIFI tags'
	MSG_CLEAR_LORA_TAGS	 = 'Clear LORA tags'
	MSG_TAG_KISS			= 'Uploading last interaction'
	MSG_TAG_STATUS		  = 'Version 2.50'
	MSG_TAG_UPLOAD_GLOBAL	= 'Uploading Global Log'
	MSG_CLEAR_GLOBAL = 'Cleared Global Log'

	###################################################################################
	# NAME		: TagsReader
	# DESCRIPTION : Deals with all TAG operations. Only works with 4 and 7 bytes.
	#			 : Payload only on NTAG
	###################################################################################
	def __init__(self, deviceId, logFunction, logFunctionLora, liveInteraction):

		self.clf = nfc.ContactlessFrontend('tty:S0:pn532')
		self.deviceId = deviceId
		self.logFunction = logFunction
		self.logFunctionLora = logFunctionLora
		self.lastTag = ''
		self.liveInteraction = liveInteraction

		#Favorite Cards
		self.favorites = []
		self.setFavorite(False)
		#Reference
		self.setReference(False, None)

		config = Config().getFavorite()
		if config is not None:
			FAVORITE_FILE = 'data/' + config['path']
			# print('Favorite file path: %s' % FAVORITE_FILE)
			try:
				#import tags list
				with open(FAVORITE_FILE, 'r') as csvfile:
					lines = csv.reader(csvfile, delimiter=',')
					lines = filter(None, lines)
					for l in lines:
						if l != None and l[1] != None and l[1] != '':
							self.favorites.append(l[1].lower())

				self.favorites.sort()
			except Exception, e:
				print('Error loading favorite csv: ' + str(e))

		# print('Load complete', self.favorites)

	# why this function!??!?
	def connected(self, tag):

		print('connected')
		print(tag.type)
		for uid, type in tagtypes:
			if isinstance(tag, type):
				print(str(attr(tag, uid)).encode("hex"))
				return
			print("error: unknown tag type")
	###################################################################################
	# NAME		: wait
	# DESCRIPTION : Wait for a new tag
	# OUTPUT	  : TAG UID
	###################################################################################

	def wait(self):

		rdwr_options = {
			'on-connect': lambda tag: False,
			'interval': 0.1
		}

		after5s = lambda: time.time() - started > 5 or time.time() - started < 0
		started = time.time();
		tag = self.clf.connect(rdwr=rdwr_options, terminate=after5s)

		return tag

	###################################################################################
	# NAME		: close
	# DESCRIPTION : Close connection with TAG reader
	###################################################################################
	def close(self):
		self.clf.close()

	###################################################################################
	# NAME		: waitAndSort
	# DESCRIPTION : Try read the UID tag.
	#			 : If it is ntag try to read the content (Payload)
	###################################################################################
	def waitAndSort(self):

		try:
			uid = None

			# only return with a valid tag
			tag = self.wait()

			print(str(tag.identifier).encode('hex'))
			print(tag.type)

			if tag.ndef:
				for record in tag.ndef.records:
					print(record)
					print('\n')

			tagId=str(tag.identifier).encode('hex')

			if( tag.ndef is not None and tag.ndef.length > 0):

				#only read the 1st record (0)
				l = tag.ndef.records
				if( len(l) > 0):
					#print(l[0].type)
					#print(l[0].name)
					#print(l[0].data.encode('hex'))
					#header = l[0].data[:3] # "\0x02, 'e', 'n' " code UTF-8

					#compare only the text
					ndefText = l[0].data[3:]
					(r, m) = self.decodePayload(ndefText)
					if(r != None):
						return r, m

			return self.logTag(tagId)

		except Exception, e:
			#print str(e)
			return self.TAG_READ_ERROR, self.MSG_TAG_OK

	###################################################################################
	# NAME		: decodePayload
	# DESCRIPTION : Decodes the payload in NTAG
	###################################################################################
	def decodePayload(self, s):

		print('decodePayload: ' + str(s))
		if s.lower() == "turnoff" or  s.lower() == "shutdown":
			return self.TAG_SHUTDOWN, self.MSG_TAG_SHUTDOWN

		if s.lower() == "update":
			return self.TAG_UPDATE, self.MSG_TAG_UPDATE

		if s.lower() == "upload" or s.lower() == "send":
			return self.TAG_UPLOAD, self.MSG_TAG_UPLOAD

		if s.lower()[:9] == "beamianid":
			return self.TAG_BEAMIAN_ID, s[9:].upper()

		if s.lower() == "favorite":
			return self.toggleFavorite()

		if s.lower()[:2] == "id":
			return self.TAG_CHANGE_ID_NAME, s[2:]

		if s.lower()[:3] == "ref":
			return self.toggleReference(s[3:])

		if s.lower() == "connect":
			return self.TAG_WIFI_CONNECT, self.MSG_TAG_WIFI_CONNECT

		if s.lower() == "disconnect":
			return self.TAG_WIFI_DISCONNECT, self.MSG_TAG_WIFI_DISCONNECT

		if s.lower() == "loraconnect":
			return self.TAG_LORA_CONNECT, self.MSG_TAG_LORA_CONNECT

		if s.lower() == "loradisconnect":
			return self.TAG_LORA_DISCONNECT, self.MSG_TAG_LORA_DISCONNECT

		if s.lower() == "starttest":
			return self.TAG_AUTO_TEST_START, self.MSG_TAG_AUTO_TEST_START

		if s.lower() == "stoptest":
			return self.TAG_AUTO_TEST_STOP, self.MSG_TAG_AUTO_TEST_STOP

		if s.lower() == "clearall":
			return self.TAG_CLEAR_ALL_TAGS, self.MSG_CLEAR_ALL_TAGS

		if s.lower() == "clearwifi":
			return self.TAG_CLEAR_WIFI_TAGS, self.MSG_CLEAR_WIFI_TAGS

		if s.lower() == "clearlora":
			return self.TAG_CLEAR_LORA_TAGS, self.MSG_CLEAR_LORA_TAGS

		if s.lower() == "kiss":
			return self.TAG_KISS, self.MSG_TAG_KISS

		if s.lower() == "status":
			return self.TAG_STATUS, self.MSG_TAG_STATUS
		
		if s.lower() == "uploadglobal":
			return self.TAG_UPLOAD_GLOBAL, self.MSG_TAG_UPLOAD_GLOBAL

		if s.lower() == "clearglobal":
			return self.TAG_CLEAR_GLOBAL, self.MSG_CLEAR_GLOBAL

		print 'Unknown payload'
		return self.TAG_READ_ERROR, self.MSG_TAG_INVALID_PAYLOAD


	###################################################################################
	# NAME		: setFavorite
	# DESCRIPTION : Set the Favorite status
	###################################################################################
	def setFavorite(self, state):
		self.waitingFavorite = state

	###################################################################################
	# NAME		: toggleFavorite
	# DESCRIPTION : Toggle favorite status
	###################################################################################
	def toggleFavorite(self):
		self.waitingReference = False

		if self.waitingFavorite:
			self.waitingFavorite = False
			return self.TAG_OK, self.MSG_TAG_OK
		else:
			self.waitingFavorite = True
			return self.TAG_FAVORITE, self.MSG_TAG_FAVORITE

	###################################################################################
	# NAME		: isWaitingFavorite
	# DESCRIPTION : The system is wainting or not for a new tag to save as favorite
	# OUTPUT	  : True  - Is waiting for another tag
	#			 : False -
	###################################################################################
	def isWaitingFavorite(self):
		return self.waitingFavorite

	###################################################################################
	# NAME		: setReference
	# DESCRIPTION : Set the Reference status
	###################################################################################
	def setReference(self, state, ref):
		self.waitingReference = state
		self.whichReference = ref

	###################################################################################
	# NAME		: toggleReference
	# DESCRIPTION : Toggle reference status
	###################################################################################
	def toggleReference(self, ref):
		self.waitingFavorite = False

		if self.waitingReference:
			if self.whichReference == ref:
				self.waitingReference = False
				self.whichReference = None
				return self.TAG_OK, self.MSG_TAG_OK
			else:
				self.whichReference = ref
				return self.TAG_REFERENCE, self.MSG_TAG_REFERENCE + ref
		else:
			self.waitingReference = True
			self.whichReference = ref
			return self.TAG_REFERENCE, self.MSG_TAG_REFERENCE + ref

	###################################################################################
	# NAME		: isWaitingReference
	# DESCRIPTION : The system is waiting or not for a new tag to save as referenced
	# OUTPUT	  : True  - Is waiting for another tag
	#			 : False -
	###################################################################################
	def isWaitingReference(self):
		return self.waitingReference

	###################################################################################
	# NAME		: logTag
	# DESCRIPTION : Save the tag ID and informations on log
	# INPUT	   : tagId - tag uid
	###################################################################################
	def logTag(self, tagId):

		if tagId == None or tagId == '':
			return self.TAG_READ_ERROR, self.MSG_TAG_OK

		timeStr = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

		if tagId in self.favorites:
			return self.toggleFavorite()
		else:
			if self.isWaitingFavorite():
				cardType = 'Favorite'
				self.setFavorite(False)
			else:
				if self.isWaitingReference():
					cardType = 'Reference' + self.whichReference
					self.setReference(False, None)
				else:
					cardType = 'Standard'
					# avoid duplicate last tag
					if self.lastTag == tagId:
						return self.TAG_OK, self.MSG_TAG_OK

		#save in csv format
		self.logFunction(tagId.upper(), timeStr, self.deviceId, cardType)
		if self.logFunctionLora is not None:
			self.logFunctionLora(tagId.upper(), timeStr, self.deviceId, cardType)

		if self.liveInteraction is not None:
			self.liveInteraction(tagId.upper(), timeStr, self.deviceId, cardType)

		self.lastTag = tagId
		print("Tag saved: " + str(tagId))

		return self.TAG_OK_ADD, self.MSG_TAG_OK

	###################################################################################
	# NAME		: setLoraLogFunc
	# DESCRIPTION : sets or unsets the logging function
	# INPUT	   :  loraLoggingFunc
	###################################################################################
	def setLoraLogFunc(self, loraLoggingFunc):
		self.logFunctionLora = loraLoggingFunc
		return

	###################################################################################
	# NAME		: changelLogFunc
	# DESCRIPTION : sets or unsets the logging function
	# INPUT	   :  loraLoggingFunc
	###################################################################################
	def setLiveLogFunc(self, liveLoggingFunc):
		self.liveInteraction = liveLoggingFunc
		return