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


class CompizFormat(Format):
    FORMAT = 'compiz'
    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_section_line,
                              self._parse_parameter_line,
                              self._parse_to_delete_line]

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

        self._current_section = OrderedDict()
        self._current_section_name = ''

        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:
            document_lines = self._get_list_of_logic_lines(document_text)
            self._lines_to_dictionary(document_lines)

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

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

        cls._section_line = (Literal('[').suppress()
                             + Optional(action_symbols, default='')('action')
                             + section_name('name')
                             + Literal(']').suppress())('section_name')

        parameter_name = originalTextFor(OneOrMore(Regex('[^=]+')))

        parameter_value = Word(printables)

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

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

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

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

            if self._current_section_name != '':
                if self._current_section_name in \
                        self._document_dictionary.keys():
                    self._document_dictionary[self._current_section_name].\
                            update(self._current_section)
                    self._current_section = OrderedDict()
                else:
                    self._item_to_add[self._current_section_name] = \
                            self._current_section
                    self._current_section = OrderedDict()
                    self._ready_to_update = True

            self._current_section_name = tuple(
                                           parsing_result.section_name.asList()
                                              )

            if self._last_comments_list != []:
                self._current_section['#'] = self._last_comments_list
                self._last_comments_list = []
        except ParseException:
            return

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

            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._current_section[key_value] = parameter_value
        except ParseException:
            return

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

            if parsing_result.action == '-':
                return

            key_value = tuple(parsing_result.parameter_name.asList())
            parameter_value = self._last_comments_list
            self._last_comments_list = []

            self._current_section[key_value] = parameter_value
        except ParseException:
            return

    def _parse_comment_line(self, line):
        '''Метод для парсинга строк содержащих комментарий.'''
        try:
            parsing_result = self._comment_line.parseString(line)
            self._match = True

            if not self._ignore_comments:
                self._last_comments_list.append(parsing_result.comment)
        except ParseException:
            return

    def _finish_method(self):
        '''Метод для завершения парсинга. В данном случае добавляет в итоговый
        словарь последнюю разобранную секцию.'''
        if self._current_section_name in self._document_dictionary.keys():
            self._document_dictionary[self._current_section_name].update(
                                                          self._current_section
                                                                        )
        else:
            self._item_to_add[self._current_section_name] =\
                    self._current_section
            self._document_dictionary.update(self._item_to_add)

        self._current_section = OrderedDict()
