import copy

from ctypeslib.codegen import typedesc
from funcs import find_unqualified_type

def _signatures_types(funcs, items):
    """Given a sequence of typedesc.Function instances, generate a set of all
    typedesc instances used in function declarations."""
    arguments = set()

    for f in funcs:
        for t in f.iterArgTypes():
            ut = find_unqualified_type(t)
            if ut in items:
                arguments.add(ut)
        ut = find_unqualified_type(f.returns)
        if ut in items:
            arguments.add(ut)

    return arguments

class TypePuller:
    def __init__(self, all):
        self._items = []
        #self._all = sorted(all, cmpitems)
        self._all = all
        self._done = set()

        # This list contains a list of struct names for which the puller will
        # not pull the members. This is an hack to avoid some cycle in
        # recursives structs declarations which refer one to each other.
        self._STRUCTS_IGNORE = ['_IO_FILE', '_IO_marker', 'yoyo11', 'yoyo12']

    def pull_fundamental(self, item):
        pass

    def pull_cv_qualified_type(self, item):
        self.pull(item.typ)
        #names.add(item.name)
        self._items.append(item)

    def pull_typedef(self, item):
        # XXX: Generate the typdef itself
        if not item in self._done:
            self._done.add(item)
            self.pull(item.typ)
            #names.add(item.name)
            self._items.append(item)

    def pull_function(self, item):
        # XXX: fix signatures_type for single item
        types = _signatures_types([item], self._all)
        #names.append(item.name)
        for t in types:
            ut = find_unqualified_type(t)
            if ut in self._all:
                self.pull(ut)
        self._items.append(item)

    def pull_function_type(self, item):
        # XXX: fix signatures_type for single item
        types = _signatures_types([item], self._all)
        #self._items.append(item)
        for t in types:
            ut = find_unqualified_type(t)
            if ut in self._all:
                self.pull(ut)

    def pull_structure(self, item):
        #names.append(item.name)
        if not item in self._done:
            self._done.add(item)
            if item.name in self._STRUCTS_IGNORE:
                # XXX: hack. We remove all members of the ignored structures,
                # to generate an opaque structure in the code genrator.
                print "Ignoring", item, item.name