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

# <one line to give the library's name and an idea of what it does.>
# Copyright (C) 2014  <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/>.
#

"""
Dla zadanej pary zdan okresla relacje, ktora moze pomiedzy ta para zachodzic.

Wejscie:
--------
 - Lista grafow referencyjnych - grafy uogolnione dla kazdej relacji
 - para zdan, a wlasciwie to para grafow zbudowanych ze zdan do porownania

Wyjscie:
--------
 - Ranking relacji dla kazdej miary (odpowiednio malejacy lub w niektorych 
   przypadkach rosnacy)


Opis dzialania:
---------------

g1, g2 - grafy zbudowane ze zdan wejsciowych
M={measure1, ...} - lista miar do podobienstwa
G={<Gf, Gt>, ...} - lista grafow uogolnionych dla kazdej relacji

for gf, gt in G:
 for m in M:
  simv=
    max(
      m.calculate(gf, g1) * m.calculate(gt, g2),
      m.calculate(gt, g1) * m.calculate(gf, g2))
  // podobienstwo miedzy <g1,g2> a <gf,gt> = simv
  d[gf, gt][m]=simv
Wypisac ranking wedlug posortowanych wartosci simv dla miar w kluczu d[gf,gt].

Przykład uruchomienia:
python entsim.py \
  -c cfg/grafon.ini \
  -l /home/pkedzia/dokumenty/Projekty/CLARIN/Entailment/semantic_relations_zestawienie_v2-used-docs.csv \
  -e /mnt/dane_eksperymenty/entailment/grafon-experiments/gen-graphs/13.01.2016/simple/grafon_graphs__mintok-1_minnode-1_remzeronodes-True_maxnode-div-8_simple \
  -p /mnt/dane_eksperymenty/entailment/paczki-lingiwsci/paczki/unzip/11 \
  -w /mnt/dane_eksperymenty/entailment/grafon-experiments/wikinews-graphs/simple/ \
  -o simple-entailment-test.csv
"""

import entailment_base as entailment
from grafon.utils.grafonconfig import GrafonConfig

import sys
import operator
import argparse
try:
  import argcomplete
except ImportError:
  argcomplete = None

def make_parser(desc=''):
  aparser = argparse.ArgumentParser(
    description = desc,
    formatter_class=argparse.RawDescriptionHelpFormatter)
  
  aparser.add_argument('-c', '--grafon-config', dest='grafon_cfg', 
                       required=True, help=('Path to grafon config.'))
  aparser.add_argument('-e', '--entailment-graphs-dir', dest='ent_graphs_dir',
                       required=True, help=(
                         'Directory where all entailment graphs represents '
                         'relations are stored'))
  aparser.add_argument('-w', '--wikinews-graphs-dir', dest='wikinews_gphs_dir',
                       required=True, help=(
                         'Directory with graphs built from wikinews.'))
  aparser.add_argument('-p', '--ling-pack-dir', dest='pack_dir', required=True,
                       help=('Directory with linguistic packages with '
                         'triples of documents. From these packages the '
                         'relations will be recognized.'))
  aparser.add_argument('-l', '--lingustic-decisions', dest='ling_decisions',
                       help=('Path to file where linguistic decisions about '
                         'the relations are stored. The file should contains '
                         'into first and second column names of documents and '
                         'seentence numbers between relation was checked. '
                         'In general filename:sentence\tfilename:sentence '
                         'the pair have to be separated with tabular.'),
                       required=False)
  aparser.add_argument('-o', '--out-res-file', dest='out_res_file',
                       required=True, help=(
                         'Output file where similarity results will be stored'))
  aparser.add_argument('--bias', default=0.001, dest='bias', help=(
                         'Bias used into writing measure values to output file'))
  if argcomplete:
    argcomplete.autocomplete(aparser)
  return aparser

def calculate_sim(g1, g2, relgraphs, measures):
  """
  Dla kazdej relacji oblicza srednia wartosc podobienstwa przekazanych miar.
  
  Zwraca slownik posortowany wedlug wartosci podobienstwa, gdzie kluczem
  jest nazwa relacji, wartoscia jest srednia wartosc podobienstwa 
  z wszystkich miar.

  dict { str : float }
  """

  res = {}
  for m in measures:
    for relname, refgraphs in relgraphs.iteritems():
      from_g = refgraphs['from']
      to_g = refgraphs['to']
      if not res.has_key(relname):
        res[relname] = 0
      res[relname] += max(
        m.calculate(from_g, g1) * m.calculate(to_g, g2),
        m.calculate(from_g, g2) * m.calculate(to_g, g1))
  for m, v in res.iteritems():
    res[m] = v/len(measures)
  return sorted(res.items(), key=operator.itemgetter(1), reverse=True)

def write_result(d1, s1, d2, s2, results, packnum, lingpack, out_res_file, bias):
  """
  Funkcja zapisuje na przekazany plik wyniki.
  Zapisuje tylko te wyniki, gdzie wartosc podobienstwa jest wieksza od zadanego
  progu.
  """
  stext1 = None
  stext2 = None
  for filename, sentnumbers, senttexts in lingpack[packnum]:
    if d1 == filename:
      stext1 = senttexts[sentnumbers.index(s1)]
    if d2 == filename:
      stext2 = senttexts[sentnumbers.index(s2)]
    if stext1 and stext2:
      break
  res_str = '{}:s{}\t{}:s{}\t{}\t{}'.format(d1, s1, d2, s2, stext1, stext2)
  rel_str = ''
  for relname, value in results:
    if value >= bias:
      rel_str += '{};'.format(relname)
  if len(rel_str) > 1:
    with open(out_res_file, 'ab') as ostream:
      ostream.write('{}\t{}\n'.format(res_str, rel_str))

def main(argv=None):
  p = make_parser(__doc__)
  args = p.parse_args()
  cfg = GrafonConfig(args.grafon_cfg)

  lp = entailment.read_pack(args.pack_dir)
  gp = entailment.make_gpack(lp, args.wikinews_gphs_dir)
  relgraphs = entailment.read_gen_graphs(args.ent_graphs_dir)
  ldec = None if not args.ling_decisions \
    else entailment.read_ling_decision(args.ling_decisions)
  gen_pack = entailment.generate_pairs(lp, lingdec=ldec)

  
  # for progress used only
  packi = 0
  packsize = len(gen_pack)
  for packnum, sentpairs in gen_pack.iteritems():
    # for progress used only
    packi += 1
    senti = 0
    sentsize = len(sentpairs)
    for (d1, s1), (d2, s2) in sentpairs:
      senti += 1
      print >> sys.stderr, '\r\tPack {} from {}, sentences {} from {}'.format(
        packi, packsize, senti, sentsize), 
      g1, g1p = gp[packnum][d1][s1]
      g2, g2p = gp[packnum][d2][s2]
      res = calculate_sim(g1, g2, relgraphs, cfg.measures())
      write_result(d1, s1, d2, s2, res, packnum, lp, args.out_res_file, args.bias)
if __name__ == '__main__':
  main()
