# -*- coding: utf-8 -*-

#
# <one line to give the library's name and an idea of what it does.>
# Copyright (C) 2015  <copyright holder> <email>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
#
import sys
import corpus2
import logging
import graphrebuilderinterface
import grafon.constants as constants
from grafon.ebuilders import netoneedgebuilder

class VerbSieBuilder(graphrebuilderinterface.GraphRebuilderInterface):
  def __init__(self, str_name='VerbSieBuilder'):
    super(VerbSieBuilder, self).__init__(str_name)

    self.tagset = corpus2.get_named_tagset('nkjp')
    self._full_pos_mask = corpus2.get_attribute_mask(self.tagset, '')
    self.__FILTER_VERBS = ["móc"]
    # WARNING! the tags below are correct only with nkjp tagset!
    self.__VERB_POS = [
      'fin', 'praet', 'impt', 'inf', 'pcon', 'pant', 
      'imps', 'winien', 'pred', 'pact', 'ppas', 'ger']
    self.__INTERP_POS = ['interp']

  def rebuild_base_graph(self, bgraph):
    """
    For each graph stored into bgraph.graph_dict try to rebuild graph
    with connecting się to nearest verb

    For rebuild grapgs calls method @see rebuild_graph
    """
    if not bgraph or not bgraph.document:
      return 
    for p in bgraph.document.paragraphs():
      for sentence in p.sentences():
        g = self.rebuild_graph(bgraph, sentence)
        if g:
          bgraph.graph_dict[sentence.id()] = g

  def rebuild_graph(self, basegraph, sentence):
    """
    This method rebuild single graph for given sentence.

    There polish word 'się' is connected to nearest, and probably appropriated
    verb word in the left or right side of 'się'. If the ver is found, then 
    new node is added to graph and old verb is removed.
    """
    tpos = 0
    g = basegraph.graph_dict.get(sentence.id())
    if not g:
      logging.error(
        'There is no graph in VerbSie builder '
        'for sentence {}'.format(sentence.id()))
      return None

    for t in sentence.tokens():
      lemma = t.get_preferred_lexeme(self.tagset).lemma_utf8()
      if lemma == 'się':
        vpos = self._find_nearest_verb(tpos, sentence)
        if vpos != sys.maxint:
          vtok = sentence.tokens()[vpos]
          for k in basegraph.node_converter.key(vtok):
            if k:
              nk = ('{} się'.format(k.encode('utf8'))).decode('utf8')
              if not g.has_node(nk):
                g.add_node(nk, attr_dict = { 
                  constants.NODE_TOKENS       : [vtok],
                  constants.NODE_COUNT        : 1,
                  constants.NODE_IS_SINGLETOK : False,
                  constants.NODE_NE_ANNOTS    : [],
                  constants.NODE_SOURCES      : [constants.NODE_FROM_TEXT]})
              else:
                g.node[nk][constants.NODE_COUNT] += 1
                g.node[nk][constants.NODE_TOKENS].append(vtok)

              for (fn, tn, data) in g.in_edges(k, data=True):
                # k = tn
                g.add_edge(fn, nk, attr_dict = data)
              for (fn, tn, data) in g.out_edges(k, data=True):
                # fn = k
                g.add_edge(nk, tn, attr_dict = data)
              # Remove verb
              if g.has_node(k):
                g.remove_node(k)

              # Remove 'się'
              for ksie in basegraph.node_converter.key(t):
                for (fn, tn, data) in g.in_edges(ksie, data=True):
                  # ksie = tn
                  g.add_edge(fn, nk, attr_dict = data)
                for (fn, tn, data) in g.out_edges(ksie, data=True):
                  # fn = ksie
                  g.add_edge(nk, tn, attr_dict = data)
                if g.has_node(ksie):
                  g.remove_node(ksie)
      tpos += 1
    return g

  def _find_nearest_verb(self, sie_pos, sentence,):
    """
    General method to find nearest verb for given 'się' position.
    """
    b_pos = self.__find_near_before_vpos(sie_pos, sentence)
    a_pos = self.__find_near_after_vpos(sie_pos, sentence)
    return b_pos if b_pos <= a_pos * 1.4 else a_pos

  def __find_near_before_vpos(self, spos, sentence):
    for p in range(spos-1, -1, -1):
      tok = sentence.tokens()[p]
      if self.__is_iterp(tok):
        return sys.maxint
      if self.__filter_token(tok):
        continue
      if self.__is_verb(tok):
        return p
    return sys.maxint

  def __find_near_after_vpos(self, spos, sentence):
    for p in range(spos, len(sentence.tokens())):
      tok = sentence.tokens()[p]
      if self.__is_iterp(tok):
        return sys.maxint
      if self.__filter_token(tok):
        continue
      if self.__is_verb(tok):
        return p
    return sys.maxint

  def __filter_token(self, token):
    """If token (lemma) is verb and should be filter 
    then returns True otherwise returns False."""
    lexeme = token.get_preferred_lexeme(self.tagset)
    pos_mask = lexeme.tag().get_masked(self._full_pos_mask)
    if self.__is_verb(token) and lexeme.lemma_utf8() in self.__FILTER_VERBS:
      return True
    return False

  def __is_verb(self, tok):
    lexeme = tok.get_preferred_lexeme(self.tagset)
    pos_mask = lexeme.tag().get_masked(self._full_pos_mask)
    return self.tagset.tag_to_symbol_string(pos_mask) in self.__VERB_POS

  def __is_iterp(self, tok):
    lexeme = tok.get_preferred_lexeme(self.tagset)
    pos_mask = lexeme.tag().get_masked(self._full_pos_mask)
    return self.tagset.tag_to_symbol_string(pos_mask) in self.__INTERP_POS