import rosebot.communicator
import socket
import time

class SocketCommunicator(rosebot.communicator.Communicator):
    """Uses a socket to send and receive messages to/from the robot
    """
    def __init__(self,
                 address,
                 connect=True,
                 wait_for_acknowledgement=True,
                 send_acknowledgement=False,
                 is_debug=False):
        self.address = address
        self.read_buffer_size = 4096
        self.bytes_read_but_not_yet_returned = bytearray()

        super().__init__(connect=connect,
                         wait_for_acknowledgement=wait_for_acknowledgement,
                         send_acknowledgement=send_acknowledgement,
                         is_debug=is_debug)

    # TODO implement a __repr__ and/or __str__

    def establish_connection(self):
        try:
            print(self.address)
            self.socket_connection = socket.socket(socket.AF_INET,
                                                   socket.SOCK_STREAM)
            self.socket_connection.connect((self.address, 2000))
        except:
            raise  # TODO Error handling.

        time.sleep(1)
        bytes_read = self.socket_connection.recv(self.read_buffer_size)
        print('Initial bytes read:', bytes_read)

        # At this point, the wifly sends to the Arduino:
        #  *HELLO* followed (possibly) by other bytes.
        # The Arduino will "eat" those bytes immediately,
        # without echoing them, so that when this Python
        # program sends a command the Arduino is ready for it.

        # The following is previous attempts to address
        # the above issue.  Will delete when we have
        # a tested approach that works.

#         print('Reading the preliminary 13 bytes:')
#         for _ in range(13):
#             byte_received = self.receive_bytes(1)
#             time.sleep(0.1)
#             print(byte_received, chr(byte_received))
#         print('Done reading the preliminary 13 bytes.')
#
#         # Send 255s repeatedly until get 3 254s in a row.
#         print('Starting 255s and 254s')
#         count = 0
#         bytes_read = bytearray()
#         while True:
#             if count >= 3:
#                 break
#             bytes_read = (bytes_read
#                           + self.socket_connection.recv(self.read_buffer_size))
#
#             if len(bytes_read) == 0:
#                 print('Sending 255 after 1 second.')
#                 time.sleep(1)
#                 self.send_bytes(bytearray([255]))
#             else:
#                 print('Read:', bytes_read)
#                 if 254 in bytes_read:
#                     break
#                 bytes_read = bytearray()

    def disconnect(self):
        """ Does whatever is needed to close the connection cleanly. """
        return self.socket_connection.shutdown(socket.SHUT_RDWR)

    def send_bytes(self, bytes_to_send):
        """
        Sends the given message to the Arduino.
        Returns the number of bytes actually sent.
          :type message: bytes or bytearray
          :rtype int
        """
        self.socket_connection.sendall(bytes_to_send)
        return len(bytes_to_send)

    def receive_bytes(self, number_of_bytes_to_return=1):
        """
        Receives from the Arduino the given number of bytes.
        Returns a byte (integer between 0 and 255) if the given
        number of bytes is 1, otherwise returns a bytearray
        containing the bytes.

        Blocking behavior is determined by
          TIMEOUT_FOR_READ_IN_SECONDS
        which was set when this object was constructed.
          :rtype byte or bytearray
        """
        num_bytes = number_of_bytes_to_return
        while True:
            if self.is_debug:
                print('bytes in buffer:',
                      self.bytes_read_but_not_yet_returned)
            if len(self.bytes_read_but_not_yet_returned) >= num_bytes:
                result = self.bytes_read_but_not_yet_returned[:num_bytes]
                self.bytes_read_but_not_yet_returned = self.bytes_read_but_not_yet_returned[num_bytes:]
                break

            bytes_read = self.socket_connection.recv(self.read_buffer_size)
            if self.is_debug:
                print('bytes read:', bytes_read)
            self.bytes_read_but_not_yet_returned += bytes_read

        if self.is_debug:
            print('result:', result)

        if len(result) == 1:
            return int(result[0])
        else:
            return result