"""
Descriptor objects for entities that are part of the LLVM project.
"""

import ConfigParser
import StringIO
import sys

from util import *

class ParseError(Exception):
    pass

class ComponentInfo(object):
    """
    Base class for component descriptions.
    """

    type_name = None

    @staticmethod
    def parse_items(items, has_dependencies = True):
        kwargs = {}
        kwargs['name'] = items.get_string('name')
        kwargs['parent'] = items.get_optional_string('parent')
        if has_dependencies:
            kwargs['dependencies'] = items.get_list('dependencies')
        return kwargs

    def __init__(self, subpath, name, dependencies, parent):
        if not subpath.startswith('/'):
            raise ValueError,"invalid subpath: %r" % subpath
        self.subpath = subpath
        self.name = name
        self.dependencies = list(dependencies)

        # The name of the parent component to logically group this component
        # under.
        self.parent = parent

        # The parent instance, once loaded.
        self.parent_instance = None
        self.children = []

        # The original source path.
        self._source_path = None

        # A flag to mark "special" components which have some amount of magic
        # handling (generally based on command line options).
        self._is_special_group = False

    def set_parent_instance(self, parent):
        assert parent.name == self.parent, "Unexpected parent!"
        self.parent_instance = parent
        self.parent_instance.children.append(self)

    def get_component_references(self):
        """get_component_references() -> iter

        Return an iterator over the named references to other components from
        this object. Items are of the form (reference-type, component-name).
        """

        # Parent references are handled specially.
        for r in self.dependencies:
            yield ('dependency', r)

    def get_llvmbuild_fragment(self):
        abstract

class GroupComponentInfo(ComponentInfo):
    """
    Group components have no semantics as far as the build system are concerned,
    but exist to help organize other components into a logical tree structure.
    """

    type_name = 'Group'

    @staticmethod
    def parse(subpath, items):
        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
        return GroupComponentInfo(subpath, **kwargs)

    def __init__(self, subpath, name, parent):
        ComponentInfo.__init__(self, subpath, name, [], parent)

    def get_llvmbuild_fragment(self):
        result = StringIO.StringIO()
        print >>result, 'type = %s' % self.type_name
        print >>result, 'name = %s' % self.name
        print >>result, 'parent = %s' % self.parent
        return result.getvalue()

class LibraryComponentInfo(ComponentInfo):
    type_name = 'Library'

    @staticmethod
    def parse(subpath, items):
        kwargs = ComponentInfo.parse_items(items)
        kwargs['library_name'] = items.get_optional_string('library_name')
        kwargs['required_libraries'] = items.get_list('required_libraries')
        kwargs['add_to_library_groups'] = items.get_list(
            'add_to_library_groups')
        return LibraryComponentInfo(subpath, **kwargs)

    def __init__(self, subpath, name, dependencies, parent, library_name,
                 required_libraries, add_to_library_groups):
        ComponentInfo.__init__(self, subpath, name, dependencies, parent)

        # If given, the name to use for the library instead of deriving it from
        # the component name.
        self.library_name = library_name

        # The names of the library components which are required when linking
        # with this component.
        self.required_libraries = list(required_libraries)

        # The names of the library group components this component should be
        # considered part of.
        self.add_to_library_groups = list(add_to_library_groups)

    def get_component_references(self):
        for r in ComponentInfo.get_component_references(self):
            yield r
        for r in self.required_libraries:
            yield ('required library', r)
        for r in self.add_to_library_groups:
            yield ('library group', r)

    def get_llvmbuild_fragment(self):
        result = StringIO.StringIO()
        print >>result, 'type = %s' % self.type_name
        print >>result, 'name = %s' % self.name
        print >>result, 'parent = %s' % self.parent
        if self.library_name is not None:
            print >>result, 'library_name = %s' % self.library_name
        if self.required_libraries:
            print >>result, 'required_libraries = %s' % ' '.join(
                self.required_libraries)
        if self.add_to_library_groups:
            print >>result, 'add_to_library_groups = %s' % ' '.join(
                self.add_to_library_groups)
        return result.getvalue()

    def get_library_name(self):
        return self.library_name or self.name

    def get_prefixed_library_name(self):
        """
        get_prefixed_library_name() -> str

        Return the library name prefixed by the project name. This is generally
        what the library name will be on disk.
        """

        basename = self.get_library_name()

        # FIXME: We need to get the prefix information from an explicit project
        # object, or something.
        if basename in ('gtest', 'gtest_main'):
            return basename

        return 'LLVM%s' % basename

    def get_llvmconfig_component_name(self):
        return self.get_library_name().lower()

class LibraryGroupComponentInfo(ComponentInfo):
    type_name = 'LibraryGroup'

    @staticmethod
    def parse(subpath, items):
        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
        kwargs['required_libraries'] = items.get_list('required_libraries')
        kwargs['add_to_library_groups'] = items.get_list(
            'add_to_library_groups')
        return LibraryGroupComponentInfo(subpath, **kwargs)

    def __init__(self, subpath, name, parent, required_libraries = [],
                 add_to_library_groups = []):
        ComponentInfo.__init__(self, subpath, name, [], parent)

        # The names of the library components which are required when linking
        # with this component.
        self.required_libraries = list(required_libraries)

        # The names of the library group components this component should be
        # considered part of.
        self.add_to_library_groups = list(add_to_library_groups)

    def get_component_references(self):
        for r in ComponentInfo.get_component_references(self):
            yield r
        for r in self.required_libraries:
            yield ('required library', r)
        for r in self.add_to_library_groups:
            yield ('library group', r)

    def get_llvmbuild_fragment(self):
        result = StringIO.StringIO()
        print >>result, 'type = %s' % self.type_name
        print >>result, 'name = %s' % self.name
        print >>result, 'parent = %s' % self.parent
        if self.required_libraries and not self._is_special_group:
            print >>result, 'required_libraries = %s' % ' '.join(
                self.required_libraries)
        if self.add_to_library_groups:
            print >>result, 'add_to_library_groups = %s' % ' '.join(
                self.add_to_library_groups)
        return result.getvalue()

    def get_llvmconfig_component_name(self):
        return self.name.lower()

class TargetGroupComponentInfo(ComponentInfo):
    type_name = 'TargetGroup'

    @staticmethod
    def parse(subpath, items):
        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
        kwargs['required_libraries'] = items.get_list('required_libraries')
        kwargs['add_to_library_groups'] = items.get_list(
            'add_to_library_groups')
        kwargs['has_jit'] = items.get_optional_bool('has_jit', False)
        kwargs['has_asmprinter'] = items.get_optional_bool('has_asmprinter',
                                                           False)
        kwargs['has_asmparser'] = items.get_optional_bool('has_asmparser',
                                                          False)
        kwargs['has_disassembler'] = items.get_optional_bool('has_disassembler',
                                                             False)
        return TargetGroupComponentInfo(subpath, **kwargs)

    def __init__(self, subpath, name, parent, required_libraries = [],
                 add_to_library_groups = [], has_jit = False,
                 has_asmprinter = False, has_asmparser = False,
                 has_disassembler = False):
        ComponentInfo.__init__(self, subpath, name, [], parent)

        # The names of the library components which are required when linking
        # with this component.
        self.required_libraries = list(required_libraries)

        # The names of the library group components this component should be
        # considered part of.
        self.add_to_library_groups = list(add_to_library_groups)

        # Whether or not this target supports the JIT.
        self.has_jit = bool(has_jit)

        # Whether or not this target defines an assembly printer.
        self.has_asmprinter = bool(has_asmprinter)

        # Whether or not this target defines an assembly parser.
        self.has_asmparser = bool(has_asmparser)

        # Whether or not this target defines an disassembler.
        self.has_disassembler = bool(has_disassembler)

        # Whether or not this target is enabled. This is set in response to
        # configuration parameters.
        self.enabled = False

    def get_component_references(self):
        for r in ComponentInfo.get_component_references(self):
            yield r
        for r in self.required_libraries:
            yield ('required library', r)
        for r in self.add_to_library_groups:
            yield ('library group', r)

    def get_llvmbuild_fragment(self):
        result = StringIO.StringIO()
        print >>result, 'type = %s' % self.type_name
        print >>result, 'name = %s' % self.name
        print >>result, 'parent = %s' % self.parent
        if self.required_libraries: