from pycomm3 import LogixDriver, CommError
from datetime import datetime as dt
from time import sleep
from random import choice
from string import ascii_lowercase

from threading import Thread
from thingsboard_gateway.connectors.connector import Connector, log
from thingsboard_gateway.tb_utility.tb_utility import TBUtility
from thingsboard_gateway.tb_utility.tb_loader import TBModuleLoader


class EthernetIPConnector(Thread, Connector):
	def __init__(self, gateway, config, connector_type):
		super().__init__()
		self.statistics = {'MessagesReceived': 0,
						   'MessagesSent': 0}  # Dictionary, will save information about count received and sent messages.
		self.__config = config  # Save configuration from the configuration file.
		self.__gateway = gateway  # Save gateway object, we will use some gateway methods for adding devices and saving data from them.
		self.setName(self.__config.get("name","%s connector " % self.get_name() + ''.join(choice(ascii_lowercase) for _ in range(5))))  # get from the configuration or create name for logs.
		log.info("Starting Custom %s connector", self.get_name())  # Send message to logger
		self.daemon = True  # Set self thread as daemon
		self.stopped = True  # Service variable for check state
		self.__connected = False  # Service variable for check connection to device
		self.__devices = {}  # Dictionary with devices, will contain devices configurations, converters for devices and serial port objects
		self.__load_converters(connector_type)  # Call function to load converters and save it into devices dictionary
		self.__connect_to_devices()  # Call function for connect to devices
		self._connector_type = connector_type
		self.utilities = TBModuleLoader.import_module(self._connector_type, "Utilities")
		log.info('Custom connector %s initialization success.', self.get_name())  # Message to logger
		log.info("Devices in configuration file found: %s ", '\n'.join(device for device in self.__devices))  # Message to logger
	
	def __connect_to_devices(self):
		for device in self.__devices:
			try:
				log.debug(self.__devices[device]["device_config"]["ip"])
				self.__devices[device]["logixdriver"] = LogixDriver(self.__devices[device]["device_config"]["ip"])
			except Exception as e:
				log.exception(e)
			else:
				self.__gateway.add_device(self.__devices[device]["device_config"]["name"], {"connector": self}, self.__devices[device]["device_config"]["type"])
				self.__connected = True
	
	def get_name(self):
		return self.name #self.__config["name"]
		
	def is_connected(self):
		return self.__connected
	
	def __load_converters(self, connector_type):
		devices_config = self.__config.get('devices')
		try:
			
			if devices_config is not None:
				for device_config in devices_config:
					if device_config.get('converter') is not None:
						log.debug(device_config)
						converter = TBModuleLoader.import_module(connector_type, device_config['converter'])
						self.__devices[device_config['name']] = {'converter': converter(device_config),
																 'device_config': device_config}
					else:
						log.error('Converter configuration for the custom connector %s -- not found, please check your configuration file.', self.get_name())
			else:
				log.error('Section "devices" in the configuration not found. A custom connector %s has being stopped.', self.get_name())
				self.close()
		except Exception as e:
			log.exception(e)
		
	def run(self):
		try:
			while True:
				if int(dt.timestamp(dt.now())) % self.__config["publishInterval"] == 0:
					log.debug("Publish Interval: %s" % dt.now())
					for device in self.__devices:
						try:
							tags = list(self.__devices[device]["device_config"]["telemetry"].keys())
							with self.__devices[device]["logixdriver"] as driver:
								data_from_device = driver.read(*tags)
							log.debug(data_from_device)
							converted_data = self.__devices[device]['converter'].convert(self.__devices[device]['device_config'], data_from_device)
							log.debug(converted_data)
							self.__gateway.send_to_storage(self.get_name(), converted_data)
						except CommError as ce:
							log.error("Error connecting to PLC")
							log.error(ce)
						except Exception as e:
							log.error("Error in for loop of run()")
							log.error(e)
					sleep(1)
		except Exception as e:
			log.exception(e)
	def open(self):
		log.info("Starting EthernetIP Connector")
		self.stopped = False
		self.start()
			
	def close(self):
		pass
		
	def on_attributes_update(self, content):
		log.info(content)
		if self.__devices.get(content["device"]) is not None:
			device_config = self.__devices[content["device"]].get("device_config")
			if device_config is not None and device_config.get("attributeUpdates") is not None:
				requests = device_config["attributeUpdates"]
				for request in requests:
					attribute = request.get("attributeOnThingsBoard")
					log.debug(attribute)
					if attribute is not None and attribute in content["data"]:
						try:
							value = content["data"][attribute]
							str_to_send = str(request["stringToDevice"].replace("${" + attribute + "}", str(value))).encode("UTF-8")
							self.__devices[content["device"]]["serial"].write(str_to_send)
							log.debug("Attribute update request to device %s : %s", content["device"], str_to_send)
							time.sleep(.01)
						except Exception as e:
							log.exception(e)
		
	def server_side_rpc_handler(self, content):
		log.debug(content)
		method_split = content["method"].split('_')
		method = None
		if len(method_split) > 0:
			method = "_".join(method_split[1:])
		
		commands = {
			"ping": self.utilities.ping,
			"write_plc": self.utilities.write_plc
		}

		if method is not None:
			#check utilities for function
			log.debug(f"Looking for {method} in Utilities")
			log.info(self.__devices)
			if method in commands.keys():
				if method == "write_plc":
					log.info(commands[method](self.__devices["Big Test Boi"]["logixdriver"],content))
				else:
					log.info(commands[method](content))
		
