/*         semantique.cpp*/
/*
*  This file is part of PRAELECTOR.
*
*  PRAELECTOR 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 2 of the License, or
*  (at your option) any later version.
*
*  COLLATINVS 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 PRAELECTOR; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* © Yves Ouvrard, 2009 - 2019
*/

/*
                                TODO
- recherche d'hyperonyme avant l'insertion d'un lien.
    https://fr.wikipedia.org/wiki/Hyperonymie
  - chercher le même lien chez tous les autres sèmes.
  - chercher le même id de liensynt, avec un super hyper ou 
    hyponyme du même lien.
- il faudrait formuler les questions "peut-on dire <hyper> <op> <hypo> ?
  donc : chercher récursivement si l'hyperonyme peut...


- voir aussi l'existence chez les autres sèmes de liens ==, et demander
  s'il y a hyper- ou hyponymie.

*/

#include <iostream>
#include <QFile>
#include <QInputDialog>
#include <QMessageBox>
#include <QTextStream>

#include <ch.h>
#include <semantique.h>

#include <QDebug>

using namespace std;



/*********************/
/*    classe Lien    */
/*********************/

Lien::Lien(QString idl, Seme* s)
{
    _sup = s;
    _id = idl;
}

bool operator==(Lien& la, Lien& lb)
{
    return la.toString() == lb.toString();
}

bool Lien::hypo()
{
    return _id.at(0) == '@';
}

QString Lien::id()
{
    return _id;
}

Seme* Lien::sup()
{
    return _sup;
}

QString Lien::toString()
{
    return QString("   %1;%2")
        .arg(_id)
        .arg(_sup->cle());
}

/*********************/
/*    classe Seme    */
/*********************/

// Un Seme est l'aspect sémantique d'un Lemme
// Il garde la mémoire des liens syntaxique dont il est le sub
// et celle des relations ontologiques transitives dont il est
// aussi le sub : est_un, est_dans, 
// TODO On peut imaginer aussi les relations symétriques

Seme::Seme(QString c, QObject* parent) : QObject(parent)
{
    _rete = qobject_cast<Semantique*>(parent);
    _cle = c;
}

bool operator==(Seme& sa, Seme& sb)
{
    return sa.cle() == sb.cle();
}

bool Seme::aLien(Lien* l)
{
    for (int i=0;i<_lSynt.count();++i)
    {
        Lien* li = _lSynt.at(i);
        if (*li == *l) return true;
    }
    // un hyperonyme a peut-être le lien
    for (int i=0;i<_lOnto.count();++i)
    {
        Lien* li = _lOnto.at(i);
        if (_rete->seme(li->sup()->cle())->aLien(l))
            return true;
    }
    return false;
}

QString Seme::cle()
{
    return _cle;
}

void Seme::ajLin(QString op, Seme* s)
{
    Lien *nl = new Lien(op, s);
    if (op.at(0) == '@')
        _lOnto.append(nl);
    else _lSynt.append(nl);
}

bool Seme::hyponyme(Seme* s)
{
    for (int i=0;i<_lOnto.count();++i)
    {
        Lien*l = _lOnto.at(i);
        if (l->sup() == s || l->sup()->hyponyme(s))
            return true;
    }
    return false;
}

int Seme::nbOnto()
{
    return _lOnto.count();
}

Lien* Seme::onto(int i)
{
    if (i >= _lOnto.count())
    {
        cerr << "seme::onto, depassement";
        return 0;
    }
    return _lOnto.at(i);
}

void Seme::rmLien(Lien* l)
{
    if (l->hypo())
    {
        int i = 0;
        while (i < _lOnto.count())
        {
            Lien* li = _lOnto.at(i);
            if (*li == *l) _lOnto.removeAt(i);
            else ++i;
        }
    }
    else
    {
        int i = 0;
        while (i < _lSynt.count())
        {
            Lien* li = _lSynt.at(i);
            if (*li == *l) _lSynt.removeAt(i);
            else ++i;
        }
    }
}

QString Seme::toString()
{
    QStringList ret;
    ret.append(_cle);
    for (int i=0;i<_lSynt.count();++i)
    {
        ret.append(_lSynt.at(i)->toString());
    }
    for (int i=0;i<_lOnto.count();++i)
    {
        ret.append(_lOnto.at(i)->toString());
    }
    return ret.join("\n");
}

/***************************/
/*    classe Semantique    */
/***************************/

Semantique::Semantique()
{
    QFile fp (Ch::chemin("/data/semantique.la"));
	if (fp.open (QIODevice::ReadOnly|QIODevice::Text))
    {
        QTextStream fluxD (&fp);
        Seme* sem = 0;
        while (!fluxD.atEnd ())
        {
	        QString ligne = fluxD.readLine();
            if (ligne.simplified().isEmpty()) continue;
            if (ligne.at(0) == '!') continue;
            if (ligne.at(0) == ' ' && sem != 0)
            {
                QStringList eclats = ligne.simplified().split(';');
                if (eclats.count() < 2)
                {
                    cerr << qPrintable(ligne)<<" ligne mal formée\n";
                }
                else sem->ajLin(eclats.at(0), seme(eclats.at(1)));
            }
            else sem = seme(ligne);
        }
        fp.close ();
    }
    //else cerr << "fichier semantique.fr introuvable";
}

void Semantique::ajLien(QString l)
{
    QStringList eclats = l.split(';');
    if (eclats.count() < 3)
    {
        cerr << "ligne " << qPrintable(l) << " mal formée\n";
        return;
    }
    QString op = eclats.at(1);
    // premier lemme
    Seme* seme1 = 0;
    if (_semes.contains(eclats.at(0)))
        seme1 = _semes.value(eclats.at(0));
    else
    {
        seme1 = new Seme(eclats.at(0), this);
        _semes.insert(seme1->cle(), seme1);
    }
    // peupler le seme
    Seme* seme2 = 0;
    if (_semes.contains(eclats.at(2)))
        seme2 = _semes.value(eclats.at(2));
    else
    {
        seme2 = new Seme(eclats.at(2), this);
        _semes.insert(seme2->cle(), seme2);
    }
    // si l'option d'enrichissement sémantique est acté
    // créer un lien nl
    Lien* nl = new Lien(op, seme2);
    if (seme1->aLien(nl))
    {
        // si le sème a déjà ce lien, abandonner
        // TODO éventuellement, augmenter le poids du lien avant de quitter.
        delete nl;
        return;
    }
    // chercher d'autes sèmes ayant déjà le lien id;seme2
    // TODO à rendre récursif : en cas de réponse positive,
    //      essayer de remonter le lien aux hyperonymes
    for (int i=0;i<_semes.count();++i)
    {
        Seme* s = seme(i);
        if (s->aLien(nl))
        {
            QString q("Peut-on dire que %1 est toujours un %2<br>"
                      "et que %2 peut être %3 de %4 ?");
            int r = QMessageBox::question(0,"",q
                                          .arg(seme1->cle())
                                          .arg(s->cle())
                                          .arg(op)
                                          .arg(seme2->cle()),
                                          QMessageBox::Yes | QMessageBox::No);
            if (r == QMessageBox::Yes)
            {
                // leme1 a s comme hyperonyme, et s supporte le lien.
                // déclarer l'hyponymie
                seme1->ajLin("@", seme2);
                // supprimer le lien
                seme1->rmLien(nl);
                // ajouter le lien à s
                ajLien(QString("%1;%2;%3")
                       .arg(s->cle())
                       .arg(op)
                       .arg(seme2->cle()));
                sauve();
                return;
            }
            r = QMessageBox::question(0,"",q
                                      .arg(s->cle())
                                      .arg(seme1->cle())
                                      .arg(op)
                                      .arg(seme2->cle()),
                                      QMessageBox::Yes | QMessageBox::No);
            if (r == QMessageBox::Yes)
            {
                // s a comme hyperonyme seme1
                // déclarer l'hyponymie
                s->ajLin("@", seme1);
                // supprimer le lien
                s->rmLien(nl);
                // ajouter le lien à seme1
                ajLien(QString("%1;%2;%3")
                       .arg(seme1->cle())
                       .arg(op)
                       .arg(seme2->cle()));
                sauve();
                return;
            }
        }
    }
    // aucune -nymie détectée, ajout simple du lien
    seme1->ajLin(op, seme2);
    sauve();
}

int Semantique::nbSemes()
{
    return _semes.count();
}

QList<Seme*> Semantique::ontLien(Lien* l)
{
    QList<Seme*> ret;
    for (int i=0;i<_semes.count();++i)
    {
        Seme* s = seme(i);
        if (s->aLien(l)) ret.append(s);
    }
    return ret;
}

void Semantique::sauve()
{
    QFile fp (Ch::chemin("/data/semantique.la"));
	if (fp.open (QIODevice::WriteOnly|QIODevice::Text))
    {
        QTextStream fluxD (&fp);
        for (int i=0;i<_semes.values().count();++i)
        {
            fluxD << "\n"<<_semes.values().at(i)->toString();
        }
        fluxD << "\n";
    }
    fp.close();
}

Seme* Semantique::seme(QString s)
{
    if (_semes.contains(s))
        return _semes.value(s);
    else
    {
        Seme* seme = new Seme(s, this);
        _semes.insert(seme->cle(), seme);
        return seme;
    }
}

Seme* Semantique::seme(int i)
{
    if (i >= _semes.count())
    {
        cerr << "erreur, i > nb. de semes";
        return 0;
    }
    return _semes.values().at(i);
}
