1# Copyright 2014-2018 the openage authors. See copying.md for legal info.
2
3# TODO pylint: disable=C,R
4
5from enum import Enum
6
7from ...log import spam
8
9
10class SectionType(Enum):
11    section_header = "header"
12    section_body = "body"
13
14
15class ContentSnippet:
16    """
17    one part of text for generated files to be saved in "file_name"
18
19    before whole source files can be written, it's content snippets
20    have to be ordered according to their dependency chain.
21
22    also, each snipped can have import requirements that have to be
23    included in top of the source.
24    """
25
26    def __init__(self, data, file_name, section, orderby=None, reprtxt=None):
27        self.data      = data       # snippet content
28        self.file_name = file_name  # snippet wants to be saved in this file
29        self.typerefs  = set()      # these types are referenced
30        self.typedefs  = set()      # these types are defined
31        self.includes  = set()      # needed snippets, e.g. headers
32        self.section   = section    # place the snippet in this file section
33        self.orderby   = orderby    # use this value for ordering snippets
34        self.reprtxt   = reprtxt    # representation text
35
36        # snippets to be positioned before this one
37        self.required_snippets = set()
38
39        # snippet content is ready by default.
40        # subclasses may require generation.
41        self.data_ready = True
42
43    def get_data(self):
44        if not self.data_ready:
45            self.generate_content()
46        return self.data
47
48    def generate_content(self):
49        # no generation needed by default
50        pass
51
52    def add_required_snippets(self, snippet_list):
53        """
54        save required snippets for this one by looking at wanted type
55        references.
56
57        the available candidates have to be passed as argument
58        """
59
60        self.required_snippets |= {
61            s for s in snippet_list if len(self.typerefs & s.typedefs) > 0
62        }
63
64        spam("snippet %s requires %s",
65             repr(self), repr(self.required_snippets))
66
67        resolved_types = set()
68        for s in self.required_snippets:
69            resolved_types |= (self.typerefs & s.typedefs)
70
71        missing_types  = self.typerefs - resolved_types
72
73        return missing_types
74
75    def get_required_snippets(self):
76        """
77        return all referenced and the snippet itself in the order they
78        need to be put in the file.
79        """
80
81        # TODO: loop detection
82        ret = list()
83
84        # sort snippets deterministically by __lt__ function
85        for s in sorted(self.required_snippets):
86            ret += s.get_required_snippets()
87
88        ret.append(self)
89        return ret
90
91    def __hash__(self):
92        """
93        hash all relevant snippet properties
94        """
95
96        return hash((
97            self.data,
98            self.file_name,
99            self.section,
100            frozenset(self.typedefs),
101            frozenset(self.typerefs),
102        ))
103
104    def __lt__(self, other):
105        """
106        comparison of two snippets for their ordering
107        """
108
109        if isinstance(other, type(self)) or isinstance(self, type(other)):
110            if not (other.orderby and self.orderby):
111                faild = self if other.orderby else other
112                raise Exception("%s doesn't have orderby member set" % (
113                    repr(faild)))
114            else:
115                ret = self.orderby < other.orderby
116                return ret
117        else:
118            raise TypeError("unorderable types: %s < %s" % (
119                type(self), type(other)
120            ))
121
122    def __eq__(self, other):
123        """
124        equality check for text snippets
125        """
126
127        if type(other) is not type(self):
128            return False
129
130        return (
131            self.file_name == other.file_name and
132            self.data      == other.data and
133            self.section   == other.section and
134            self.typedefs  == other.typedefs and
135            self.typerefs  == other.typerefs
136        )
137
138    def __repr__(self):
139        if self.reprtxt:
140            data = " = %s" % self.reprtxt
141        elif self.data:
142            data = " = %s..." % repr(self.data[:25])
143        else:
144            data = ""
145
146        return "%s(file=%s)%s" % (self.__class__.__name__,
147                                  self.file_name, data)
148
149    def __str__(self):
150        if self.data_ready:
151            return "".join((
152                repr(self), ", "
153                "data = '", str(self.data), "'"
154            ))
155        else:
156            return "".join((repr(self), ": lazy generation pending"))
157