#!/usr/bin/python3

import re
import sys

from zeichenklassen import v, c, p, f, n

class Betonung:

    def __init__(self, namen_markieren, mehr_elisionen):
        self.namen_markieren = namen_markieren
        self.mehr_elisionen = mehr_elisionen

        self.häufige_elisionen = self.lese_datei('lib/elisionen.txt')
        self.häufige_aphäresen = self.lese_datei('lib/aphäresen.txt')

        self.häufig_unbetont = self.häufige_elisionen | self.häufige_aphäresen | set(['ine', 'sône'])
        self.betonte_wortarten = self.lese_datei('lib/betonte-wortarten.txt')
        self.unbetonte_wortarten = self.lese_datei('lib/unbetonte-wortarten.txt')
        self.unbetonte_adverbien = self.lese_datei('lib/unbetonte-adverbien.txt')
        self.unbetonte_praefixe = self.lese_datei('lib/unbetonte-präfixe.txt')

        self.verbpartikeln = self.lese_datei('lib/verbpartikeln.txt')
        self.nomensuffixe = self.lese_datei('lib/nomensuffixe.txt')
        self.adjektivsuffixe = self.lese_datei('lib/adjektivsuffixe.txt')
        self.erste_silbe_betont = self.lese_datei('lib/Betonung-Silbe1.txt')
        self.zweite_silbe_betont = self.lese_datei('lib/Betonung-Silbe2.txt')

        # Umwandlung in reguläre Ausdrücke
        self.verbpartikeln = rf"^({'|'.join(self.verbpartikeln)}).*{v}.*$"
        self.nomensuffixe = rf"^.*?{v}.*?({'|'.join(self.nomensuffixe)})$"
        self.adjektivsuffixe = rf"^.*?{v}.*?({'|'.join(self.adjektivsuffixe)})$"
        self.erste_silbe_betont = rf"^({'|'.join(self.erste_silbe_betont)})$"
        self.zweite_silbe_betont = rf"^({'|'.join(self.zweite_silbe_betont)})$"

        self.komposita = {}
        with open('lib/komposita.txt', encoding='utf-8') as file:
            for line in file:
                teile = line.lower().split()
                self.komposita[''.join(teile)] = teile


    @staticmethod
    def lese_datei(dateiname):
        words = set()
        with open(dateiname, encoding='utf-8') as file:
            for line in file:
                # Kommentare entfernen
                line = re.sub(r'\s*#.*', '', line.strip())
                if line:
                    words.update(line.split())
        return words


    def betonte_silben(self, silben, tag):
        if len(silben) == 1:
            return [0]  # Betonung auf der 1. Silbe

        # unregelmäßige Betonungen erkennen
        word = ''.join(silben)
        if re.match(self.zweite_silbe_betont, word):
            return [1]  # Betonung auf der 2. Silbe
        if re.match(self.erste_silbe_betont, word):
            return [0]  # Betonung auf der 1. Silbe

        # Komposita
        if word in self.komposita:
            teilwort = self.komposita[word]
            if teilwort[0] == 'ze':
                return [1]

            # Übertragung der Betonung auf die Silbenfolge
            result = []
            start = 0
            teilwortnummer = 0
            for i in range(1, len(silben)+1):
                teilsilben = silben[start:i]
                teil = ''.join(teilsilben)
                if teil == teilwort[teilwortnummer]:
                    result += [start + pos for pos in self.betonte_silben(teilsilben, tag)]
                    start = i
                    teilwortnummer += 1
            if result:
                return result

        if tag[0] == 'N' and len(silben) > 3 and word.endswith('îe'):
            return [0, len(silben)-2] # Ka'radî'e

        # unbetonte Präfixe erkennen
        if re.match(r'V|NA|ADJ|AVD|MWLpart', tag) and silben[0] in self.unbetonte_praefixe:
            if word == 'gebet' and tag.startswith('V'):
                return [0]  # Verb:  'gebet
            if len(silben) > 3 and silben[1] not in self.unbetonte_praefixe:
                result = self.betonte_silben(silben[1:], tag)
                result = [i+1 for i in result]
                return result  # er-bermec-lîche
            if len(silben) == 2 and re.match(rf'{c}*en?$', silben[1]):
                return [0]  # 'ge be  'ge hen  'er ne
            return [1]      # ge 'tan  ge 'we sen  ge 'wer

        # # Pronomialadjektive erkennen
        # if silben[0] == 'da' and tag in ['PAVD|PAVAPDIA', 'PAVD|PAVAP', 'AVD', 'APPR']:
        #     return [1]  # Betonung auf der 2. Silbe

        # # Adverbien, die mit al beginnen
        # if (silben[0] == 'al' and silben[1][0] != 'l' and 'e' not in silben[1] and
        #     re.match(r'(ADJ|AV)' , tag)):
        #     return [1]  # Betonung auf der 2. Silbe

        # # Adverbien, die mit ie beginnen
        # if silben[0] == 'ie' and tag.startswith('AV'):
        #     return [1]  # Betonung auf der 2. Silbe

        # # Wörter, die mit ze beginnen
        # # zesamen
        # if silben[0] == 'ze' and re.match(r'(AV|VV|APPR\|NA)', tag):
        #     return [1]  # Betonung auf der 2. Silbe
        # # zervallenen
        # if silben[0] == 'zer' and re.match(r'(ADJ|VV)', tag):
        #     return [1]  # Betonung auf der 2. Silbe

        # # enheine, deheine
        # if silben[0] in ['en', 'de'] and tag == 'DIA':
        #     return [1]  # Betonung auf der 2. Silbe

        # unbetonte Negationspartikeln
        # vom Tagger erkannte Negationspartikeln
        if word.startswith('en') and tag.startswith('PTKNEG|'):
            return [1]  # Betonung auf der 2. Silbe

        # vom Tagger nicht erkannte Negationspartikeln und Verbpräfix en/em
        if (re.search(r'(V|ITJ)', tag) and silben[0] == 'en' and
            (len(silben) > 2 or silben[-1] in ['tuot', 'galt', 'ist', 'was', 'wart', 'bern', 'bôt', 'brast', 'phalch', 'phienc'])):
            return [1]      # en 'ist

        # Verben, die mit a beginnen
        if silben[0] == 'a' and tag.startswith('VV'):
            return [1]  # Betonung auf der 2. Silbe

        # Verben, die auf ...ieren enden
        if tag.startswith('VV') and re.match(r'{c}*{v}+{c}*ier[ten]+$', word):
            return [1]  # Betonung auf der 2. Silbe:  tjostierte

        def ist_einsilbig(affix):
            return re.match(rf'{c}*{v}+{c}*$', affix)

        # Partikelverben und deren Derivate mit Nebenakzent
        if re.match(r'VV|NA|ADJ|AVD', tag):
            if re.match(r'vürst(en?)?$', word):
                return [0]
            if (match := re.match(self.verbpartikeln, word)):
                prefix = match.group(1)
                return [0, 1] if ist_einsilbig(prefix) else [0, 2]

        # Nomen mit besonderen Suffixen, welche einen Nebenakzent tragen
        if tag.startswith('NA'):
            if (match := re.match(self.nomensuffixe, word)):
                suffix = match.group(1)
                return [0, len(silben)-1] if ist_einsilbig(suffix) else [0, len(silben)-2]

        # Adjektive mit besonderen Suffixen, welche einen Nebenakzent tragen
        if re.match(r'A[DV]', tag) and len(silben) > 2:
            if silben[0] == 'un':  # unmüezic
                betonung = [pos + 1 for pos in self.betonte_silben(silben[1:], tag)]
                return betonung
            if (match := re.match(self.adjektivsuffixe, word)):
                suffix = match.group(1)
                betonung = [0, len(silben)-1] if ist_einsilbig(suffix) else [0, len(silben)-2]
                return betonung

        # Default: Betonung auf der 1. Silbe
        return [0]


    @staticmethod
    def ist_kurz(silbe):
        ''' Silbe mit einzelnem Vokal am Ende außer âêîôûæœ '''
        return re.match(rf'^(.*{c})?[aeiouäöü]$', silbe)


    def letter_sequence(self, position, silben, word, tag, tag2):
        ''' erzeugt die Zeichenfolge für ein Wort '''

        # einsilbiges Wort
        if len(silben) == 1:
            kurz = self.ist_kurz(word)
            betont   = tag in self.betonte_wortarten
            unbetont = tag in self.unbetonte_wortarten

            if word in ['ouch', 'noch']:
                return 'A' if 'APPR' in tag2 else 'a'

            if tag.endswith('AVD') and word in self.unbetonte_adverbien:
                return 'u' if kurz else 'a'

            if kurz:
                return 'U' if betont else 'υ' if unbetont else 'u'
            else:  # Silbe ist lang
                return 'A' if betont else 'α' if unbetont else 'a'

        # mehrsilbiges Wort
        bet_silben = self.betonte_silben(silben, tag)
        letters = ''
        for i, silbe in enumerate(silben):
            kurz = self.ist_kurz(silbe)
            betont = (i in bet_silben)
            schwa = re.match(rf'{c}*e{c}*$', silbe)
            l = ('κ' if schwa and kurz and not betont else
                 'λ' if schwa and not kurz and not betont else
                 'k' if kurz and not betont else
                 'l' if not kurz and not betont else
                 'K' if kurz and betont else
                 'L')
            letters += l

        # großgeschriebene Wörter als Eigennamen markieren
        if (self.namen_markieren and word[0].isupper() and
            letters[0] in 'KL' and letters[1] not in 'κλ'):
            letters = letters[0] + '<name>' + letters[1:]

        if word in self.häufig_unbetont:
            letters = letters.lower()  # keine natürliche Betonung

        return letters


    def moegliche_elisionen_markieren(self, letters, silben, silben2, lc_word, word2):

        s = silben[-1] + ' ' + silben2[0]

        # Elision vor nachfolgendem Vokal
        if re.match(rf'^{c}+e {v}', s):
            # âne alle --> ân. alle
            letters +=  '<elision2>'
        elif not self.mehr_elisionen:
            pass
        
        elif lc_word in self.häufige_elisionen:
                letters += '<elision1>'
        elif lc_word in ['hêrre', 'vrouwe']:
            if re.match(r'([A-ZÂÊÎÔÛÄÖÜ]|künec|künig[iî]n|swester)', word2):
                letters = letters.lower() + '<elision1>' # keine natürlich Betonung
            elif re.match(rf'{v}', word2):
                letters += '<elision1>'
            else:
                letters += '<elision2>'
        elif re.search(rf'{v}({c}+)(ere[mnst]|genes)$', lc_word):
            # jeger(e)n  morgen(e)s iuwer(e)m
            letters += '<elision3>'
        elif re.search(r'([aeiouâêîôû]|ie)[rd]e[nmst]$', lc_word):
            # verlor(e)n var(e)n
            letters += '<elision3>'
        elif ((match := re.match(rf'.*{v}({c})\1?e(d|r|t|st)e[nmrs]?$', lc_word)) and
              match.group(1) != match.group(2)):
            # schönesten -> schönsten
            letters = letters[:-1] + '<elision3>' + letters[-1]
        elif re.search(rf'{v}(l)\1?ene[nmrs]?$', lc_word):
            # zerfall(e)nen
            letters = letters[:-1] + '<elision3>' + letters[-1]
        elif len(silben) > 1 and re.match(rf'^[gz]*et .', s):
            # Maget --> Mag.t
            letters += '<elision3>'
        elif re.match(rf'^[zhrg]*en .', s):
            # gehen --> gehn  ganzen --> ganzn  varen -> varn  gezogen -> gezogn
            letters += '<elision3>'
        return letters


    def make_input(self, alle_silben, words, tags):
        ''' erzeugt eine Folge von Buchstaben, die Silbentypen repräsentieren:
        L lange betonte Silbe
        l lange unbetonte Silbe
        λ lange unbetonte Silbe
        K kurze betonte Silbe
        k kurze unbetonte Silbe
        κ kurze unbetonte Silbe
        W einsilbiges oft betontes Wort
        w einsilbiges weniger oft betontes Wort
        v einsilbiges sehr selten betontes Wort
        fügt auch Marker für mögliche Elisionen ein.
        '''
        result = ''
        for i, tuple  in enumerate(zip(alle_silben, alle_silben[1:] + [['']],
                                       words, words[1:] + ('',),
                                       tags, tags[1:] + ('',))):
            silben, silben2, word, word2, tag, tag2 = tuple

            # Wort kleinschreiben und Klitika entfernen
            lc_word = re.sub(rf"^({c}+')?(\w+?)('{c}+)?$", r'\2', word.lower())

            letters = self.letter_sequence(i, silben, word, tag, tag2)
            letters = self.moegliche_elisionen_markieren(letters, silben, silben2, lc_word, word2)

            if silben2[0] != '':  # kein Zeilenende
                letters += ' '    # Das Leerzeichen markiert eine Wortgrenze

            # Möglichkeit der Aphärese markieren
            if word2 in self.häufige_aphäresen:
                # keine Aphärese direkt nach einer Elision
                if not re.match(r'.*<elision[12]> *$', letters):
                    letters += '<aphaerese>'

            result += letters
        return result
