# vim: fileencoding=utf-8
#
from .base_format import Format
from ..template_engine import ParametersContainer
from collections import OrderedDict
from pyparsing import Word, Literal, alphanums, originalTextFor,\
                      OneOrMore, ParseException, restOfLine, Group, Optional,\
                      Regex


class KernelFormat(Format):
    FORMAT = 'kernel'
    EXECUTABLE = False

    _initialized = False

    comment_symbol = '#'

    def __new__(cls, *args, **kwargs):
        if not cls._initialized:
            cls._initialize_parser()
        return super().__new__(cls)

    def __init__(self, document_text: str,
                 template_path,
                 ignore_comments=False,
                 join_before=False,
                 add_header=False,
                 already_changed=False,
                 parameters=ParametersContainer()):
        processing_methods = [self._parse_comment_line,
                              self._parse_parameter_line,
                              self._parse_to_delete_line]

        super().__init__(processing_methods)
        self._ignore_comments = ignore_comments
        self._join_before = join_before
        self._comments_processing = True

        self._last_comments_list = []

        if add_header and not ignore_comments:
            self.header, document_text = self._get_header_and_document_text(
                                               document_text,
                                               template_path,
                                               already_changed=already_changed)
        else:
            self.header = ''

        document_text = document_text.strip()
        if document_text == '':
            self._document_dictionary = OrderedDict()
        else:
            documentLines = self._get_list_of_logic_lines(document_text)
            self._lines_to_dictionary(documentLines)

    @classmethod
    def _initialize_parser(cls):
        '''Метод для инициализации парсеров.'''
        parameter_name = Word(alphanums+'_')('parameter_name')

        parameter_value = originalTextFor(Regex(r'\S+(\s+\S+)*')
                                          )('parameter_value')

        action_symbols = (Literal('!') | Literal('-'))

        cls._parameter_line = (Group(Optional(action_symbols,
                                              default='')('action')
                                     + parameter_name('name')
                                     )('parameter_name')
                               + Literal('=').suppress()
                               + parameter_value('parameter_value'))

        cls._parameter_to_delete = (Group(action_symbols('action')
                                          + parameter_name('name')
                                          )('parameter_name')
                                    + restOfLine.suppress())

        cls._comment_line = originalTextFor(
                             Literal(cls.comment_symbol).suppress()
                             + Regex(r'.*'))('Comment')
        cls._initialized = True

    def _parse_parameter_line(self, line):
        '''Метод для парсинга строк содержащих параметр.'''
        try:
            self._item_to_add = OrderedDict()
            parsing_result = self._parameter_line.parseString(line)

            key_value = tuple(parsing_result.parameter_name.asList())
            parameter_value = parsing_result.parameter_value

            parameter_value = self._last_comments_list + [parameter_value]
            self._last_comments_list = []

            self._item_to_add[key_value] = parameter_value
            self._ready_to_update = True
            self._match = True
        except ParseException:
            return

    def _parse_to_delete_line(self, line):
        '''Метод для парсинга строк, подлежащих удалению, т.е. для которых
        указано имя параметра со знаком !, и опционально присутствует значение
        параметра.'''
        try:
            self._item_to_add = OrderedDict()
            parsing_result = self._parameter_to_delete.parseString(line)

            if parsing_result.action == '-':
                return

            key_value = (parsing_result.parameter_name.action,
                         parsing_result.parameter_name.name.upper())
            parameter_value = self._last_comments_list
            self._last_comments_list = []

            self._item_to_add[key_value] = parameter_value
            self._ready_to_update = True
            self._match = True
        except ParseException:
            return

    def _parse_comment_line(self, line):
        '''Метод для парсинга строк содержащих комментарий.'''
        try:
            result = self._comment_line.parseString(line)
            self._match = True
            if not self._ignore_comments:
                self._last_comments_list.append(result.Comment)
        except ParseException:
            return
