# Copyright (c) 2013 Hesky Fisher
# See LICENSE.txt for details.
#
# TODO: Convert non-ascii characters to UTF8
# TODO: What does ^ mean in Eclipse?
# TODO: What does #N mean in Eclipse?
# TODO: convert supported commands from Eclipse

"""Parsing an RTF/CRE dictionary.

RTF/CRE spec:
https://web.archive.org/web/20201017075356/http://www.legalxml.org/workgroups/substantive/transcripts/cre-spec.htm

"""

import re
import string

from plover import __version__ as plover_version
from plover.dictionary.helpers import StenoNormalizer
from plover.formatting import ATOM_RE
from plover.steno_dictionary import StenoDictionary

from .rtfcre_parse import parse_rtfcre


HEADER = (
    r"{\rtf1\ansi{\*\cxrev100}"
    r"\cxdict{\*\cxsystem Plover %s}"
    r"{\stylesheet{\s0 Normal;}}"
) % plover_version


class RegexFormatter:
    def __init__(self, spec_list, escape_fn):
        self._escape_fn = escape_fn
        self._format_for_lastindex = [None]
        pattern_list = []
        for pattern, replacement in spec_list:
            num_groups = len(self._format_for_lastindex)
            pattern_groups = re.compile(pattern).groups
            if pattern_groups:
                needed = []
                for token in string.Formatter().parse(replacement):
                    field_name = token[1]
                    if not field_name:
                        continue
                    group = int(field_name)
                    assert 0 <= group <= pattern_groups
                    needed.append(group + num_groups)
            else:
                pattern = "(" + pattern + ")"
                pattern_groups = 1
                needed = []
            for n in range(pattern_groups):
                self._format_for_lastindex.append((needed, replacement))
            pattern_list.append(pattern)
        self._format_rx = re.compile("|".join(pattern_list))

    def format(self, s):
        m = self._format_rx.fullmatch(s)
        if m is None:
            return None
        needed, replacement = self._format_for_lastindex[m.lastindex]
        return replacement.format(*(self._escape_fn(m.group(g)) for g in needed))


class TranslationFormatter:
    # fmt: off
    TO_ESCAPE = (
        (r"([\\{}])", r"\\\1"   ),
        (r"\n\n"    , r"\\par " ),
        (r"\n"      , r"\\line "),
        (r"\t"      , r"\\tab " ),
    )
    ATOMS_FORMATTERS = (
        # Note order matters!
        (r"{\.}"                       , r"{{\cxp. }}"                 ),
        (r"{!}"                        , r"{{\cxp! }}"                 ),
        (r"{\?}"                       , r"{{\cxp? }}"                 ),
        (r"{\,}"                       , r"{{\cxp, }}"                 ),
        (r"{:}"                        , r"{{\cxp: }}"                 ),
        (r"{;}"                        , r"{{\cxp; }}"                 ),
        (r"{\^ \^}"                    , r"\~"                         ),
        (r"{\^-\^}"                    , r"\_"                         ),
        (r"{\^\^?}"                    , r"{{\cxds}}"                  ),
        (r"{\^([^^}]*)\^}"             , r"{{\cxds {0}\cxds}}"         ),
        (r"{\^([^^}]*)}"               , r"{{\cxds {0}}}"              ),
        (r"{([^^}]*)\^}"               , r"{{{0}\cxds}}"               ),
        (r"{-\|}"                      , r"\cxfc "                     ),
        (r"{>}"                        , r"\cxfl "                     ),
        (r"{ }"                        , r" "                          ),
        (r"{&([^}]+)}"                 , r"{{\cxfing {0}}}"            ),
        (r"{(.*)}"                     , r"{{\*\cxplovermeta {0}}}"    ),
    )
    TRANSLATIONS_FORMATTERS = (
        (r"{\*}"                       , r"{{\*\cxplovermacro retrospective_toggle_asterisk}}"),
        (r"{\*!}"                      , r"{{\*\cxplovermacro retrospective_delete_space}}"),
        (r"{\*\?}"                     , r"{{\*\cxplovermacro retrospective_insert_space}}"),
        (r"{\*\+}"                     , r"{{\*\cxplovermacro repeat_last_stroke}}"),
        (r"=undo"                      , r"\cxdstroke"                 ),
        (r"=(\w+(?::.*)?)"             , r"{{\*\cxplovermacro {0}}}"   ),
    )
    # fmt: on

    def __init__(self):
        self._to_escape = [
            (re.compile(pattern), replacement)
            for pattern, replacement in self.TO_ESCAPE
        ]
        self._atom_formatter = RegexFormatter(self.ATOMS_FORMATTERS, self.escape)
        self._translation_formatter = RegexFormatter(
            self.TRANSLATIONS_FORMATTERS, self.escape
        )

    def escape(self, text):
        for rx, replacement in self._to_escape:
            text = rx.sub(replacement, text)
        return text

    def format(self, translation):
        s = self._translation_formatter.format(translation)
        if s is not None:
            return s
        parts = []
        for atom in ATOM_RE.findall(translation):
            atom = atom.strip()
            if not atom:
                continue
            s = self._atom_formatter.format(atom)
            if s is None:
                s = self.escape(atom)
            parts.append(s)
        return "".join(parts)


class RtfDictionary(StenoDictionary):
    def _load(self, filename):
        with open(filename, "rb") as fp:
            text = fp.read().decode("cp1252")
        with StenoNormalizer(filename) as normalize_steno:
            self.update(parse_rtfcre(text, normalize=normalize_steno))

    def _save(self, filename):
        translation_formatter = TranslationFormatter()
        with open(filename, "w", encoding="cp1252", newline="\r\n") as fp:
            print(HEADER, file=fp)
            for s, t in self.items():
                s = "/".join(s)
                t = translation_formatter.format(t)
                entry = r"{\*\cxs %s}%s" % (s, t)
                print(entry, file=fp)
            print("}", file=fp)
