"""
Copyright 2008-2011 Free Software Foundation, Inc.
This file is part of GNU Radio

GNU Radio Companion is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

GNU Radio Companion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

from .Element import Element
from . import odict


class Connection(Element):

    def __init__(self, flow_graph, porta, portb):
        """
        Make a new connection given the parent and 2 ports.

        Args:
            flow_graph: the parent of this element
            porta: a port (any direction)
            portb: a port (any direction)
        @throws Error cannot make connection

        Returns:
            a new connection
        """
        Element.__init__(self, flow_graph)
        source = sink = None
        #separate the source and sink
        for port in (porta, portb):
            if port.is_source(): source = port
            if port.is_sink(): sink = port
        if not source: raise ValueError('Connection could not isolate source')
        if not sink: raise ValueError('Connection could not isolate sink')
        busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2
        if not busses == 0: raise ValueError('busses must get with busses')

        if not len(source.get_associated_ports()) == len(sink.get_associated_ports()):
            raise ValueError('port connections must have same cardinality');
        #ensure that this connection (source -> sink) is unique
        for connection in self.get_parent().get_connections():
            if connection.get_source() is source and connection.get_sink() is sink:
                raise LookupError('This connection between source and sink is not unique.')
        self._source = source
        self._sink = sink
        if source.get_type() == 'bus':

            sources = source.get_associated_ports();
            sinks = sink.get_associated_ports();

            for i in range(len(sources)):
                try:
                    flow_graph.connect(sources[i], sinks[i]);
                except:
                    pass

    def __str__(self):
        return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%(
            self.get_source().get_parent(),
            self.get_source(),
            self.get_sink().get_parent(),
            self.get_sink(),
        )

    def is_connection(self): return True

    def validate(self):
        """
        Validate the connections.
        The ports must match in type.
        """
        Element.validate(self)
        platform = self.get_parent().get_parent()
        source_domain = self.get_source().get_domain()
        sink_domain = self.get_sink().get_domain()
        if (source_domain, sink_domain) not in platform.get_connection_templates():
            self.add_error_message('No connection known for domains "%s", "%s"'
                                   % (source_domain, sink_domain))
        too_many_other_sinks = (
            source_domain in platform.get_domains() and
            not platform.get_domain(key=source_domain)['multiple_sinks'] and
            len(self.get_source().get_enabled_connections()) > 1
        )
        too_many_other_sources = (
            sink_domain in platform.get_domains() and
            not platform.get_domain(key=sink_domain)['multiple_sources'] and
            len(self.get_sink().get_enabled_connections()) > 1
        )
        if too_many_other_sinks:
            self.add_error_message(
                'Domain "%s" can have only one downstream block' % source_domain)
        if too_many_other_sources:
            self.add_error_message(
                'Domain "%s" can have only one upstream block' % sink_domain)

    def get_enabled(self):
        """
        Get the enabled state of this connection.

        Returns:
            true if source and sink blocks are enabled
        """
        return self.get_source().get_parent().get_enabled() and \
            self.get_sink().get_parent().get_enabled()

    #############################
    # Access Ports
    #############################
    def get_sink(self): return self._sink
    def get_source(self): return self._source

    ##############################################
    ## Import/Export Methods
    ##############################################
    def export_data(self):
        """
        Export this connection's info.

        Returns:
            a nested data odict
        """
        n = odict()
        n['source_block_id'] = self.get_source().get_parent().get_id()
        n['sink_block_id'] = self.get_sink().get_parent().get_id()
        n['source_key'] = self.get_source().get_key()
        n['sink_key'] = self.get_sink().get_key()
        return n