# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""Gettext PO(T) parser

Parses gettext po and pot files.
"""


import re

from .base import (
    CAN_SKIP,
    Entity,
    BadEntity,
    Parser
)


class PoEntityMixin:

    @property
    def val(self):
        return (
            self.stringlist_val
            if self.stringlist_val
            else self.stringlist_key[0]
        )

    @property
    def key(self):
        return self.stringlist_key

    @property
    def localized(self):
        # gettext denotes a non-localized string by an empty value
        return bool(self.stringlist_val)

    def __repr__(self):
        return self.key[0]


class PoEntity(PoEntityMixin, Entity):
    pass


# Unescape and concat a string list
def eval_stringlist(lines):
    return ''.join(
        (
            l
            .replace(r'\\', '\\')
            .replace(r'\t', '\t')
            .replace(r'\r', '\r')
            .replace(r'\n', '\n')
            .replace(r'\"', '"')
        )
        for l in lines
    )


class PoParser(Parser):
    # gettext l10n fallback at runtime, don't merge en-US strings
    capabilities = CAN_SKIP

    reKey = re.compile('msgctxt|msgid')
    reValue = re.compile('(?P<white>[ \t\r\n]*)(?P<cmd>msgstr)')
    reComment = re.compile(r'(?:#.*?\n)+')
    # string list item:
    # leading whitespace
    # `"`
    # escaped quotes etc, not quote, newline, backslash
    # `"`
    reListItem = re.compile(r'[ \t\r\n]*"((?:\\[\\trn"]|[^"\n\\])*)"')

    def __init__(self):
        super().__init__()

    def createEntity(self, ctx, m, current_comment, white_space):
        start = cursor = m.start()
        id_start = cursor
        try:
            msgctxt, cursor = self._parse_string_list(ctx, cursor, 'msgctxt')
            m = self.reWhitespace.match(ctx.contents, cursor)
            if m:
                cursor = m.end()
        except BadEntity:
            # no msgctxt is OK
            msgctxt = None
        if id_start is None:
            id_start = cursor
        msgid, cursor = self._parse_string_list(ctx, cursor, 'msgid')
        id_end = cursor
        m = self.reWhitespace.match(ctx.contents, cursor)
        if m:
            cursor = m.end()
        val_start = cursor
        msgstr, cursor = self._parse_string_list(ctx, cursor, 'msgstr')
        e = PoEntity(
            ctx,
            current_comment,
            white_space,
            (start, cursor),
            (id_start, id_end),
            (val_start, cursor)
        )
        e.stringlist_key = (msgid, msgctxt)
        e.stringlist_val = msgstr
        return e

    def _parse_string_list(self, ctx, cursor, key):
        if not ctx.contents.startswith(key, cursor):
            raise BadEntity
        cursor += len(key)
        frags = []
        while True:
            m = self.reListItem.match(ctx.contents, cursor)
            if not m:
                break
            frags.append(m.group(1))
            cursor = m.end()
        if not frags:
            raise BadEntity
        return eval_stringlist(frags), cursor
