# -*- coding: utf-8
"""
pdfreactor.parsecfg._tokeninfo: TokenInfo class

(helper class for the .parse module)
"""

# Python compatibility:
from __future__ import absolute_import

from six.moves import StringIO

__author__ = "Tobias Herp <tobias.herp@visaplan.com>"

# Standard library:
# from tokenize import untokenize  (customize, perhaps?)
from tokenize import (
    COMMENT,
    ENDMARKER,
    INDENT,
    NAME,
    NEWLINE,
    NL,
    OP,
    STRING,
    TokenError,
    generate_tokens,
    tok_name,
    )


class TokenInfo(tuple):
    """
    A class to provide information about a token
    as represented by a tuple, generated by the tokenize.generate_tokens
    function.

    This is mainly for internal use by the generate_token_groups function.

    >>> txt = 'empty = "";'
    >>> toklist = list(generate_tokens(StringIO(txt).readline))
    >>> len(toklist)
    5
    >>> name, op, val, semic, term = toklist

    >>> name_info = TokenInfo(name)
    >>> name_info
    (NAME empty)
    >>> name_info.is_terminator
    False

    >>> op_info = TokenInfo(op)
    >>> op_info
    (OP '=')
    >>> op_info.is_terminator
    False

    >>> val_info = TokenInfo(val)
    >>> val_info
    (STRING "")
    >>> val_info.is_terminator
    False

    >>> semic_info = TokenInfo(semic)
    >>> semic_info
    (OP ';')
    >>> semic_info.is_terminator
    True

    >>> term_info = TokenInfo(term)
    >>> term_info
    (ENDMARKER)
    >>> term_info.is_terminator
    True

    >>> txt = 'disableLinks(true)'
    >>> toklist = list(generate_tokens(StringIO(txt).readline))
    >>> len(toklist)
    5
    >>> name, op1, val, op2, term = toklist

    >>> name_info = TokenInfo(name)
    >>> name_info
    (NAME disableLinks)
    >>> name_info.is_terminator
    False

    >>> op1_info = TokenInfo(op1)
    >>> op1_info
    (OP '(')
    >>> op1_info.is_terminator
    False
    >>> op1_info.opens_brace
    True
    >>> op1_info.expects_brace
    ')'

    >>> val_info = TokenInfo(val)
    >>> val_info
    (NAME true)
    >>> val_info.is_terminator
    False

    >>> op2_info = TokenInfo(op2)
    >>> op2_info
    (OP ')')
    >>> op2_info.is_terminator
    False
    >>> op2_info.opens_brace
    False
    >>> op2_info.closes_brace
    True
    >>> op2_info.expects_brace
    Traceback (most recent call last):
      ...
    KeyError: ')'

    >>> term_info = TokenInfo(term)
    >>> term_info
    (ENDMARKER)
    >>> term_info.is_terminator
    True

    """
    @property
    def text(self):
        return self[1]

    @property
    def ttype(self):
        """
        The token type for our group is the type of the first token
        """
        return self[0]

    @property
    def tname(self):
        """
        The name of our "token type", as given by the tok_name dict
        """
        return tok_name[self.ttype]

    def __repr__(self):
        ttype = self.ttype
        if self.is_terminator:
            txt = self.text.strip()
            if txt:
                return '(%s %r)' % (self.tname, txt)
            else:
                return '(%s)' % (self.tname,)
        elif ttype == OP:
            mask = '(%s %r)'
        else:
            mask = '(%s %s)'
        return mask % (self.tname, self.text)

    @property
    def is_terminator(self):
        """
        Would this group of tokens terminate a statement?
        """
        ttype = self.ttype
        if ttype == OP:
            return self.text == ';'
        elif ttype == NAME:
            return False
        else:
            text = self.text.strip()
            return not text

    @property
    def _braces_dict(self):
        return {
            '{': '}',
            '(': ')',
            '[': ']',
            }

    @property
    def opens_brace(self):
        if self.ttype != OP:
            return False
        return self.text in self._braces_dict

    @property
    def expects_brace(self):
        """
        This will yield a KeyError if not .opens_brace!
        """
        return self._braces_dict[self.text]

    @property
    def closes_brace(self):
        if self.ttype != OP:
            return False
        return self.text in list(self._braces_dict.values())


if __name__ == '__main__':
    # Standard library:
    import doctest
    doctest.testmod()
