# -*- coding: utf-8 -*-
import general_manager as gen_man
import orth_type_case_manager as otc_manager


class NpAdjpManager(gen_man.GeneralManager):
    '''
    Manager fraz np/adjp, pozwala na wykonywanie operacji na frazach tego typu
    '''

    def __init__(self, np_adjp_phrase):
        gen_man.GeneralManager.__init__(self, np_adjp_phrase)

    # -----------------------------------------------
    # metody operujace na pozycjach wzgledem poczatku zdania, nie fazy
    # 0 oznacza pozycje - poczatek - wzgledem zdania, nie wzgledem frazy
    def agp_at(self, position):
        '''
        Zwraca Agp na zadanej pozycji w zadniu
        Kontroluje wyjscie poza granice zdania
        '''
        if self.is_outside_ann_phrase(position):
            return None
        return self.agp_at_pos(position)

    def agp_at_pos(self, position):
        '''
        Zwraca Agp na zadanej pozycji w zadniu
        UWAGA! Nie kontroluje wyjscia poza granice zdania
        '''
        for agp in self.np_adjp().agps():
            if position in agp.segments():
                return agp
        return None


    # -----------------------------------------------
    # metody operujace na pozycjach wzgledem poczatku frazy, nie zdania
    # 0 oznacza pozycje - poczatek - wzgledem frazy, nie wzgledem zdania
    def get_agp_at(self, position):
        '''
        Zwraca obiekt AgP dla dnej pozycji wzgledem poczatku frazy NP/AdjP
        Kontroluje granice frazy NP/AdjP
        '''
        if self.is_outside_ann_phrase(position):
            return None
        return self.get_agp_at_pos(position)

    def get_agp_at_pos(self, position):
        '''
        Zwraca obiekt AgP dla dnej pozycji wzgledem poczatku frazy NP/AdjP
        UWAGA! Nie kontroluje granic frazy NP/AdjP!!
        '''
        agp_seg_nr = self.np_adjp().segments()[position]
        for agp in self.np_adjp().agps():
            if agp_seg_nr in agp.segments():
                return agp
        return None

    def is_agp_head_at(self, position):
        '''
        Sprawdza czy na zadanej pozycji znajduje sie jakas glowa AgP.
        W przypadku, kiedy wychodzimy za granice frazy zwracana jest wartosc falszu
        Podajemy tylko wartosci z przedzialu <0, len(segments())>
        '''
        return self.is_inside_ann_phrase(position) and self.is_agp_head_at_pos(
            position)

    def is_agp_head_at_pos(self, position):
        '''
        UWAGA!!
            Nie sprawdzamy czy wychodzimy poza fraze!
            Nalezy uwazac przy krzystaniu z tej metody
            Nalezy recznie kontrolowac graice frazy.

        Metoda is_agp_head_at dodatkowo wprowadza kontrole nad wyjsciem poza fraze!
        '''
        return self.np_adjp().segments()[position] in self.agp_heads()

    def get_right_agp_head(self, position):
        '''
        Sprawdza czy na prawo od zadanej pozycji wystepuje jakas glowa AgP
        Zwraca AgP w ktorym znajduje sie
        '''
        if self.is_outside_ann_phrase(position):
            return None
        h_idx = self.np_adjp().get_relative_agp_heads_pos().index(position)
        # TODO Sprawdzic testami czy jest OK!!!!
        if h_idx + 1 < len(self.np_adjp().get_relative_agp_heads_pos()) - 1:
            return self.np_adjp().get_relative_agp_heads_pos()[h_idx + 1]
        return None

    def get_left_agp_head(self, rel_position):
        '''
        Sprawdza czy na lewo od zadanej pozycji wystepuje jakas glowa AgP
        Pozycja relatywna wzgledem NP nie zdania!
        Zwraca AgP w ktorym znajduje sie
        '''
        if self.is_outside_ann_phrase(rel_position) or rel_position == 0:
            return None
        h_idx = self.np_adjp().get_relative_agp_heads_pos().index(rel_position)
        if h_idx < 1:
            return None
        return self.np_adjp().get_relative_agp_heads_pos()[h_idx - 1]

    def get_left_agp_np_head(self, position):
        '''
        Zwraca pozycje glowy AgP/NP najblizej na lewo (pomija PP!)
        '''
        if self.is_outside_ann_phrase(position):
            return None

        h_idx = self.np_adjp().get_relative_agp_heads_pos().index(position)
        for hidx in range(h_idx - 1, -1, -1):
            agp = self.get_agp_at(
                self.np_adjp().get_relative_agp_heads_pos()[hidx])
            if not agp.is_pp():
                return self.np_adjp().get_relative_agp_heads_pos()[hidx]
        return None

    def get_right_agp_np_head(self, position):
        '''
        Zwraca pozycje glowy AgP/NP najblizej na prawo (pomija PP!)
        '''
        if self.is_outside_ann_phrase(position):
            return None

        for hidx in range(position + 1,
                          len(self.np_adjp().get_relative_agp_heads_pos())):
            agp = self.get_agp_at(
                self.np_adjp().get_relative_agp_heads_pos()[hidx])
            if not agp.is_pp():
                return self.np_adjp().get_relative_agp_heads_pos()[hidx]
        return None

    def is_any_right_agp_head(self, position):
        '''
        Sprawdza czy na prawo od pozycji znajduje sie dowolny AgP
        Zwraca pradwe jezeli tak, przeciwnie zwraca falsz
        '''
        return self.get_right_agp_head(position) != None

    def is_any_left_agp_head(self, position):
        '''
        Sprawdza czy na lewo od pozycji znajduje sie dowolny AgP
        Zwraca pradwe jezeli tak, przeciwnie zwraca falsz
        '''
        return self.get_left_agp_head(position) != None

    def is_same_agp(self, pos1, pos2):
        '''
        Sprawdza czy pos1 i pos2 wskazuje na ten sam AgP
        '''
        agp1 = self.get_agp_at(pos1)
        agp2 = self.get_agp_at(pos2)
        return agp1 == agp2

    def sort_agps(self):
        '''
        Sortuje liste Agp/PP w kolejnosci wystepowania glow
        '''
        agps = self.np_adjp().agps()
        rel_pos = self.np_adjp().get_relative_agp_heads_pos()
        agps.sort(key=lambda a: a.head())
        return agps

    # PP
    def get_first_prep_in_pp(self, position, tagset):
        '''
        Zwraca pozycje (wzgledem poczatku frazy NP) pierwszego prep w PP na zadanej pozycji
        '''
        agp = self.get_agp_at(position)
        if agp == None:
            return None
        for s in agp.segments():
            prep_pos = self.np_adjp().segments().index(s)
            tok_idx = self.np_adjp().segments()[prep_pos]
            token = self.np_adjp().annotated_sentence().tokens()[tok_idx]
            lexeme = token.get_preferred_lexeme(tagset)
            tok_mask = lexeme.tag().get_masked(self.np_adjp().prep_mask)
            if tok_mask == self.np_adjp().prep_mask:
                return prep_pos
        return None

    def get_left_agp_head_with_class_and_case(self, position, tagset, classes,
                                              case):
        '''
        Zwraca pozycje glowy AgP/PP/NP dla ktorej znana jest jedna z klas z listy oraz znany jest przypade
        '''
        if self.is_outside_ann_phrase(position):
            return None
        h_idx = self.np_adjp().get_relative_agp_heads_pos().index(position)
        otcman = otc_manager.OrthTypeCaseManager(self.np_adjp())

        for hidx in range(h_idx - 1, -1, -1):
            if otcman.is_tag_at_pos(
                    self.np_adjp().get_relative_agp_heads_pos()[hidx], tagset,
                    case):
                for cl in classes:
                    if otcman.is_tag_at_pos(
                            self.np_adjp().get_relative_agp_heads_pos()[hidx],
                            tagset, cl):
                        return self.np_adjp().get_relative_agp_heads_pos()[hidx]
        return None
