"""
PhraseStructureSymbol:

A class which stores information associated with
individual phrase structure symbols.

Contains phrase structure rules which are
associated with a given symbol.
"""

class PhraseStructureSymbol:
    """
    PS Symbol:

    A class which stores information associated with individual
    phrase structure symbols.

    Symbols can be any type, as long as there is an equality
    operator defined on that type.

    Functions:
    - __init__(self, symbol, is_terminal):
                Initialization dunder method.

    - add_rule(self, symbol):
                Adds a phrase-structure rule to this symbol.

    - add_rule(self, symbol1, symbol2):
                Adds a phrase-structure rule to this symbol.

    - add_parent(self, parent_symbol):
                Adds a parent reference to this symbol.

    - get_rules(self):
                Returns the set of rules associated with this symbol.

    - get_terminal_rules(self):
                Returns the subset of rules associated with this symbol
                which are terminal (by CNF standards, this means the
                symbol routed to has no rules of its own). At this
                level, this only really means the set of rules which
                are 1-tuples. This class has no way to access other
                PhraseStructureSymbol instances directly.

    -get_intermediate_rules(self):
                Returns the subset of rules associated with this symbol
                which are intermediate, AKA not terminal. (By CNF
                standards, this mean the symbols routed to are not
                themselves terminal). At this level, this only really
                means the set of rules which are 2-tuples. This class
                has no way to access other PhraseStructureSymbol instances
                directly.

    - get_parents(self):
                Returns the set of parent symbols, which reference
                this symbol.

    - terminal(self):
                Returns value of is_terminal.

    Variables:
    - symbol(any type): The data stored in this node of
                        the phrase structure. Normally,
                        this is a string. Also the
                        reference by which rules access
                        this class from the main handler.
    - rules(set): The collection of tuples which contain
                  reference labels for other PS_Symbol
                  classes, which are used in generating
                  strings of symbols as outputs of
                  the grammar.
    - parents(set): The collection of reference labels
                    which indicate symbols who route
                    to this instance of the class.
    - is_terminal(bool): Whether or not the symbol is
                         allowed to have rules.
                         In terms of graphs, indicates
                         if this node is 'terminal'.
    """

    def __init__(self, symbol, is_terminal):
        """
        Sets up local class variables. symbol represents the
        symbol used for the class, and is_terminal defines
        whether the symbol has no children in the phrase
        structure graph. If is_terminal is enabled,
        no rules can be added to the symbol.

        Parameters:
        - symbol(any type): The reference data which is both associated with this
                            class as its reference within larger handlers as its
                            label. It is also the data which will be outputted
                            in strings generated by the phrase-structure system
                            (assumming this symbol is terminal).

        - is_terminal(bool): Denotes whether this is a terminal symbol within
                             the phrase-structure. I.e. if it has no rules,
                             only parents, and can be outputted by symbol
                             strings generated by the PhraseStructure class.

        Returns:
            None

        """
        self.symbol = symbol
        self.rules = set()
        self.parents = set()
        self.is_terminal=is_terminal


    def add_terminal_rule(self, symbol):
        """
        Makes a rule which replaces this symbol with a terminal one.

        Parameters:
        - symbol: The reference data used to refer to the symbol
                  routed to by this symbol when generating symbol strings.

        Returns:
            int: 0 if the rule either already exists or has been added.
                 -1 if the current instance of this class is terminal.
        """
        if self.is_terminal:
            return -1
        self.rules.add((symbol,))
        return 0


    def add_intermediate_rule(self, symbol1, symbol2):
        """
        Makes a rule which replaces this symbol with two others.

        Parameters:
        - symbol1: The reference data used to refer to the
                   first of two symbols routed to when this
                   rule is used in the PhraseStructure class,
                   when generated symbol strings.
        - symbol2: The reference data used to refer to the
                   second of the two symbols routed to.

        Returns:
            int: 0 if the rule either already exists or has been added.
            -1 if the current instance of this class is terminal.
        """
        if self.is_terminal:
            return -1
        self.rules.add((symbol1, symbol2))
        return 0

    def add_rule(self, symbol1, symbol2=None, use_root_as_symbol2=False):
        if use_root_as_symbol2 is False:
            if symbol2 is None:
                return self.add_terminal_rule(symbol1)
            else:
                return self.add_intermediate_rule(symbol1, symbol2)
        return self.add_intermediate_rule(symbol1, symbol2)


    def add_parent(self, parent_symbol):
        """
        Adds parent_symbol to the set of parents the symbol can
        be generated from.

        Parameters:
        - parent_symbol: The reference data used to refer to the
                         symbol which is being added, which should
                         have a rule routing that symbol to this
                         instance in the phrase-structure.

        Returns:
            None
        """
        self.parents.add(parent_symbol)

    def get_rules(self):
        """
        Returns the set of rules associated with this symbol.

        Parameters:
            None

        Returns:
            set: The set of symbol reference data used to find
                 the symbols being routed to by this rule
                 in the PhraseStructure class.
        """
        return self.rules

    def get_intermediate_rules(self):
        """
        Returns the set of rules for this symbol which are 2-tuples.

        Parameters:
            None

        Returns:
            set: The set of rules which are 2-tuples in the rule set.
        """
        intermediate_rules = set()
        for rule in self.rules:
            if len(rule) == 2:
                intermediate_rules.add(rule)
        return intermediate_rules

    def get_terminal_rules(self):
        """
        Returns the set of rules for this symbol which are 1-tuples.

        Parameters:
            None

        Returns:
            set: The set of rules which are 1-tuples in the rule set.
        """
        terminal_rules = set()
        for rule in self.rules:
            if len(rule) == 1:
                terminal_rules.add(rule)
        return terminal_rules


    def get_parents(self):
        """
        Returns the set of parents which (should) refer to this symbol.

        Parameters:
            None

        Returns:
            set: The set of symbols which refer to this symbol within their rule set.
        """
        return self.parents


    def terminal(self):
        """
        Returns is_terminal.

        Parameters:
            None

        Returns:
            bool: The value of is_terminal.
        """
        return self.is_terminal
