# coding=utf8
from __future__ import unicode_literals, absolute_import, print_function

import re
from straight.parser.common import Position, ParsingException, Box


class DocString(object):

    def __init__(self, conf_args, straight_text, rest_text):
        self.conf_args = conf_args
        self.straight_text = straight_text
        self.rest_text = rest_text

    @classmethod
    def parse(cls, docstring):
        return parse(docstring)

    def __repr__(self):
        return 'DocString(conf_args={}, ...)'.format(repr(self.conf_args))


_parse_re = re.compile(r'({-#|#-}|\n|\r)')
PRAGMA_BEGIN = '{-#'
PRAGMA_END = '#-}'


def parse(docstring):
    sp = _parse_re.split(docstring)

    line = 1
    column = 0
    next_column = 0

    in_pragma = Box(False)
    pragma_text = Box(None)

    entered_straight = Box(False)
    in_straight = Box(False)

    straight_text_list = Box()
    rest_text_list = Box(list())

    straight_text = Box()
    rest_text = Box()

    conf_args = Box()

    def err(message='Unknown Error'):
        return ParsingException(
            position=Position(line=line, column=column),
            message=message,
            )

    def enter_straight(conf):
        if ~in_straight:
            raise err("Nested Straight Context")
        if ~entered_straight:
            raise err("More than one Straight Context found")
        in_straight << True
        entered_straight << True
        conf_args << conf
        straight_text_list << [PRAGMA_BEGIN, ~pragma_text, PRAGMA_END]
        rest_text_list.value.append('\n\n')

    def leave_straight():
        if not ~in_straight:
            raise err("Not in Straight Context")
        in_straight << False

    def parse_pragma():
        words = pragma_text.value.strip().split()
        if len(words) < 2:
            return

        name = words[0]
        if name != 'Straight':
            return

        command = words[1]
        if command == 'Configuration':
            enter_straight(words[2:])
        elif command == 'End':
            leave_straight()
        else:
            return

    for token in sp:
        column = next_column
        next_column = column + len(token)

        if token == '\n' or token == '\r':
            line += 1
            next_column = 0

        if ~in_straight:
            straight_text_list.value.append(token)
        else:
            rest_text_list.value.append(token)

        if token == PRAGMA_END:
            if not ~in_pragma:
                raise err("Unexpected Token " + repr(token))
            in_pragma << False
            parse_pragma()
            pragma_text << None
            continue

        if ~in_pragma:
            pragma_text.value += token

        if token == PRAGMA_BEGIN:
            if ~in_pragma:
                raise err("Nested Pragma")
            in_pragma << True
            pragma_text << ''
            continue

    if ~entered_straight:
        straight_text << (''.join(~straight_text_list) + '\n')
    rest_text << (''.join(~rest_text_list) + '\n')

    return DocString(
        ~conf_args,
        ~straight_text,
        ~rest_text
    )

