from src.comms_client import *

import os

# <class 'numpy.bool_'>

class example_client(CommClient):
    """
	this class provides an example implementation of a comms_client.
	Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified.
	"""

    def start(self):
        super().start()

    def __init__(self, fault_func, streams, try_interval=2):
        """
		initialises the class and tries to connect to the client.
		"""
        super().__init__(fault_func, streams, try_interval)

        # Explicitly connect
        if not self.connect():
            # hardware or infra is down -- needs fixing first
            fault_func()
            return

    def connect(self):
        """
		this function provides a location for the code neccecary to connect to the client
		"""
        self.streams.debug_stream(os.path.dirname(os.path.abspath(__file__)))
        self.streams.debug_stream("the example client doesn't actually connect to anything silly")

        self.connected = True  # set connected to true
        return True  # if succesfull, return true. otherwise return false

    def disconnect(self):
        self.connected = False  # always force a reconnect, regardless of a successful disconnect
        self.streams.debug_stream("disconnected from the 'client' ")

    def _setup_annotation(self, annotation):
        """
		this function gives the client access to the comm client annotation data given to the attribute wrapper.
		The annotation data can be used to provide whatever extra data is necessary in order to find/access the monitor/control point.

		the annotation can be in whatever format may be required. it is up to the user to handle its content
		example annotation may include:
		- a file path and file line/location
		- COM object path
		"""

        # as this is an example, just print the annotation
        self.streams.debug_stream("annotation: {}".format(annotation))

    def _setup_value_conversion(self, attribute):
        """
		gives the client access to the attribute_wrapper object in order to access all
		necessary data such as dimensionality and data type
		"""

        if attribute.dim_y > 1:
            dims = (attribute.dim_y, attribute.dim_x)
        else:
            dims = (attribute.dim_x,)

        dtype = attribute.numpy_type

        return dims, dtype

    def _setup_mapping(self, dims, dtype):
        """
		takes all gathered data to configure and return the correct read and write functions
		"""

        value = numpy.zeros(dims, dtype)

        def read_function():
            self.streams.debug_stream("from read_function, reading {} array of type {}".format(dims, dtype))
            return value

        def write_function(write_value):
            self.streams.debug_stream("from write_function, writing {} array of type {}".format(dims, dtype))
            value = write_value

        self.streams.debug_stream("created and bound example_client read/write functions to attribute_wrapper object")
        return read_function, write_function

    def setup_attribute(self, annotation=None, attribute=None):
        """
		MANDATORY function: is used by the attribute wrapper to get read/write functions.
		must return the read and write functions
		"""

        # process the comms_annotation
        self._setup_annotation(annotation)

        # get all the necessary data to set up the read/write functions from the attribute_wrapper
        dims, dtype = self._setup_value_conversion(attribute)

        # configure and return the read/write functions
        read_function, write_function = self._setup_mapping(dims, dtype)

        # return the read/write functions
        return read_function, write_function