class UDPDispatcher(object):
    #implements(IUDPDispatcher)

    def __init__(self, udp_client = None, settings = None, message_handler = None, message_template = None, message_xml = None):
        #holds the details of the message, or how the messages should be sent,
        #built, and read

        self.packets_in = 0
        self.packets_out = 0

        self.circuit_manager = CircuitManager()
        self.data_unpacker = DataUnpacker()

        #the ID of the packet we most recently received
        self.receive_packet_id = -1

        if udp_client == None:
            self.udp_client = NetUDPClient()
        else:
            self.udp_client = udp_client

        self.udp_client.start_udp_connection()

        # allow the settings to be passed in
        # otherwise, grab the defaults
        if settings != None:
            self.settings = settings
        else:
            self.settings = Settings()

        # allow the passing in of message_template.xml as a file handle
        if not message_template:
            self.message_template = None
        else:
            if isinstance(message_template, file):
                self.message_template = message_template
            else:
                log.warning("%s parameter is expected to be a filehandle, it is a %s. \
                        Using the embedded message_template.msg" % (message_template, type(message_template)))
                self.message_template = None

        if not message_xml:
            self.message_xml = MessageDotXML()
        else:
            self.message_xml = message_xml

        self.helpers = Helpers()

        # allow the packet_handler to be passed in
        # otherwise, grab the defaults
        if message_handler != None:
            self.message_handler = message_handler
        elif self.settings.HANDLE_PACKETS:
            from pyogp.lib.base.message.message_handler import MessageHandler
            self.message_handler = MessageHandler()

        # set up our parsers
        self.udp_deserializer = UDPMessageDeserializer(self.message_handler, 
                                                        self.settings,
                                                        message_template = self.message_template)
        self.udp_serializer = UDPMessageSerializer(message_template = self.message_template)

    def find_circuit(self, host):
        circuit = self.circuit_manager.get_circuit(host)
        if circuit == None:
            #there is a case where we want to return None,
            #when the last packet was protected
            circuit = self.circuit_manager.add_circuit(host, self.receive_packet_id)

        return circuit

    def receive_check(self, host, msg_buf, msg_size):
        #determine if we have any messages that can be received through UDP
        #also, check and decode the message we have received
        recv_packet = None
        #msg_buf, msg_size = self.udp_client.receive_packet(self.socket)

        #we have a message
        if msg_size > 0:

            #determine sender
            #host = self.udp_client.get_sender()
            circuit = self.find_circuit(host)
            if circuit == None:
                raise exc.CircuitNotFound(host, 'preparing to check for packets')

            self.packets_in += 1

            recv_packet = self.udp_deserializer.deserialize(msg_buf)

            #couldn't deserialize
            if recv_packet == None:

                # if its sent as reliable, we should ack it even if we aren't going to parse it
                # since we can skip parsing the packet in self.udp_deserializer

                # this indicate reliable
                send_flags = ord(msg_buf[0])
                packet_id = self.data_unpacker.unpack_data(msg_buf, MsgType.MVT_U32, 1, endian_type=EndianType.BIG)

                # queue the ack up
                circuit.collect_ack(packet_id)

                return None

            #Case - trusted packets can only come in over trusted circuits
            if circuit.is_trusted and \
                recv_packet.trusted == False:
                return None

            circuit.handle_packet(recv_packet)

            if self.settings.ENABLE_UDP_LOGGING:
                if self.settings.ENABLE_BYTES_TO_HEX_LOGGING:
                    hex_string = '<=>' + self.helpers.bytes_to_hex(msg_buf)
                else:
                    hex_string = ''
                if self.settings.ENABLE_HOST_LOGGING:
                    host_string = ' (%s)' % (host)
                else:
                    host_string = ''
                if not self.settings.PROXY_LOGGING:
                    logger.debug('Received packet%s : %s (%s)%s' % (host_string, recv_packet.name, recv_packet.packet_id, hex_string))

            if self.settings.HANDLE_PACKETS:
                self.message_handler.handle(recv_packet)

        return recv_packet

    def send_reliable(self, message, host, retries):
        """ Wants to be acked """
        #sets up the message so send_message will add the RELIABLE flag to
        #the message
        return self.__send_message(message, host, reliable=True, retries=retries)

    def send_retry(self, message, host):
        """ This is a retry because we didn't get acked """
        #sets up the message so send_message will add the RETRY flag to it
        return self.__send_message(host, message, retrying=True)                

    def send_message(self, message, host):
        return self.__send_message(message, host)

    def __send_message(self, message, host, reliable=False, retries=0, retrying=False):
        """ Sends the message that is currently built to the desired host """
        #make sure host is OK (ip and address aren't null)
        if host.is_ok() == False:
            return

        if isinstance(message,Message):
            packet = message
        else:
            packet = message()

        # enable monitoring of outgoing packets
        if self.settings.HANDLE_OUTGOING_PACKETS:
            self.message_handler.handle(packet)

        #use circuit manager to get the circuit to send on
        circuit = self.find_circuit(host)

        if reliable == True:
            circuit.prepare_packet(packet, PackFlags.LL_RELIABLE_FLAG, retries)
            if circuit.unack_packet_count <= 0:
                self.circuit_manager.unacked_circuits[host] = circuit
        elif retrying == True:
            circuit.prepare_packet(packet, PackFlags.LL_RESENT_FLAG)
        else:
            circuit.prepare_packet(packet)

        try:
            send_buffer = self.udp_serializer.serialize(packet)

            if self.settings.ENABLE_UDP_LOGGING:
                if packet.name in self.settings.UDP_SPAMMERS and self.settings.DISABLE_SPAMMERS:
                    pass
                else:
                    if self.settings.ENABLE_BYTES_TO_HEX_LOGGING:
                        hex_string = '<=>' + self.helpers.bytes_to_hex(send_buffer)
                    else:
                        hex_string = ''
                    if self.settings.ENABLE_HOST_LOGGING:
                        host_string = ' (%s)' % (host)
                    else:
                        host_string = ''
                    logger.debug('Sent packet    %s : %s (%s)%s' % (host_string, packet.name, packet.packet_id, hex_string))

            #TODO: remove this when testing a network
            self.udp_client.send_packet(send_buffer, host)

            self.packets_out += 1

            return send_buffer

        except AssertionError:
            pass

        except Exception, error:
            logger.warning("Error trying to serialize the following packet: %s" % (packet))
            traceback.print_exc()

            return