package DialML;

import alice.tuprolog.*;

import java.util.*;

/**
 * <p>Stratégies </p>
 * <p>Contient toutes les stratégies de dialogues et les stratégies locales que les agents peuvent utiliser </p>
 * @author Yousfi Monod Mehdi
 * @version 1.0
 */

public class Strategies {

  private AgentCog moi,lui;

  private boolean btrace=true;

  private RoleFctl rfctl;

  boolean other_sat=false; // l'interlocuteur est-il satisfait ?

  Vector buff_mess; // buffer contenant les messages provenant l'autre agent

  public Strategies(AgentCog m, AgentCog l) {
    moi=m;
    lui=l;
    buff_mess=new Vector();
  }

  public void setRfctl(RoleFctl r) {
    rfctl=r;
  }


  /** notre stratégie basique d'enseignement de la leçon
   * @param lesson La liste ordonnée des formules à enseigner
   */
  public void teachLesson(Vector lesson) {
    int rf;
    Struct[] implic;
    Enumeration e=lesson.elements();
    while (e.hasMoreElements()) {
      implic=(Struct[])e.nextElement();
      trace("Enseignement de la donnée \""+VectTheory.getLogicData(implic)+
            "\" à "+lui.getNom()+".");
      teachImplic(implic);
      trace("Attente de satisfaction ou de questions...");
      do {
        if (buff_mess.size()>1) {
            rf=((Integer)buff_mess.remove(0)).intValue();
            implic=(Struct[])buff_mess.remove(0);
            rfctl.processMessage(rf,implic);
        }
      } while (!other_sat);
      trace("Élève satisfait.");
      other_sat=false;

    }

    // le maître est satisfait : la leçon a été enseignée
    moi.world.sendMsg(lui,10,new Struct[2]);
  }

  public void teachImplic(Struct[] implic) {
    moi.world.sendMsg(lui,0,implic);
  }

  /** notre stratégie basique d'apprentissage de la leçon
   *
   */
  public void learnLesson() {
    int rf;
    Struct[] implic;
    do {
      if (buff_mess.size()>0) {
        rf=((Integer)buff_mess.remove(0)).intValue();
        implic=(Struct[])buff_mess.remove(0);
        rfctl.processMessage(rf,implic);
      }
    } while (!other_sat);
  }

  public void addMessage(int rf, Object o) {
    buff_mess.insertElementAt(o,0);
    buff_mess.insertElementAt(new Integer(rf),0);
  }

  /** Stratégie de gestion de la contradiction
   * @param implic Implication susceptible d'introduire la contradiction
   */

  public void gereContr(Struct[] implic) {
    Vector contr;
    Struct[] implic1=getContr(implic);

    if (!VectTheory.isFact(implic)) {
      // si c'est une implication
      if ((contr=getPathImplic(implic1)).size()!=0) {
        trace("Donnée \"" +
              moi.vectTheory.getLogicKnowledge(implic[0], implic[1]) +
              "\" contradictoire avec la BC.");
        if (!lui.getNom().equals("Créateur")) {
          supprContr(contr);
        }
      }
    }
    // si c'est un fait
    else {
      // si l'élève possède le fait contraire
      if (moi.vectTheory.contains(implic1)) {
        trace("Donnée \"" + VectTheory.getLogicData(implic) + "\" fausse donc supprimée.");
        delData(implic);
      }
      Vector preds;
      if ((preds=moi.vectTheory.getPredsCst(implic1[1])).size()!=0) {
        trace("Donnée \"" +
              moi.vectTheory.getLogicFact(implic[1]) +
              "\" contradictoire avec la BC.");
        Struct pred = VectTheory.getGenPred(implic1[1]), pred1, pred2;
        Struct implic2[]=new Struct[2],implic3[];
        Enumeration e = preds.elements();
        implic2[1]=pred;
        // pour chaque prédicat validés par la même constante que celle du fait fourni
        while (e.hasMoreElements()) {
          pred1 = (Struct) e.nextElement();
          pred2 = VectTheory.getGenPred(pred1);
          implic2[0]=pred2;
          if ((contr=getPathImplic(implic2)).size()!=0) {
            implic3=new Struct[2];
            implic3[0]=new Struct("");
            implic3[1]=pred1;
            verifValid(implic3);
            // supprimer les éventuelles implications fausses
            supprContr(contr);
          }
        }
      }
    }
  }

  /** Retourne le chemin allant de la prémisse jusqu'à la conclusion
   *
   */

  public Vector getPath(Struct premisse, Struct conclusion) {
    Vector res=new Vector();
    getPathRec(premisse,conclusion,res);
    return res;
  }

  // Partie récursive de getPath
  private boolean getPathRec(Struct premisse, Struct conclusion, Vector res) {
    Struct prem,concl;
    Struct[] implic=new Struct[2];
    Enumeration e=moi.vectTheory.elements();
    while (e.hasMoreElements()) {
      implic = (Struct[]) e.nextElement();
      prem = implic[0];
      concl = implic[1];
      if (premisse.equals(prem) &&
          !containsConcl(res,concl)) {
        if (concl.equals(conclusion)) {
          res.addElement(implic);
          return true;
        }
        res.addElement(implic);
        if (getPathRec(concl, conclusion,res)) return true;
      }
    }
    if (res.size()!=0) res.removeElementAt(res.size()-1);
    return false;
  }

  // l'ensemble v d'implications contient-il la conclusion fournie ?
  private boolean containsConcl(Vector v, Struct conclusion) {
    Struct[] implic;
    Struct concl;
    Enumeration e=v.elements();
    while (e.hasMoreElements()) {
      implic = (Struct[]) e.nextElement();
      concl = implic[1];
      if (concl.equals(conclusion)) return true;
    }
    return false;
  }


  public Vector getPathImplic(Struct[] implic) {
    return (getPath(implic[0],implic[1]));
  }

  /** Retourne l'implication contraire
   * @param implic1 p -> q
   * @return p -> non(q)
   */
  public Struct[] getContr(Struct[] implic1) {
    Struct[] implic2=new Struct[2];
    implic2[0]=implic1[0];
    if (implic1[1].getName().equals("not")) implic2[1]=(Struct)implic1[1].getArg(0);
    else implic2[1]=new Struct("not",implic1[1]);
    return implic2;
  }

  // supprime la contradiction de la BC
  private void supprContr(Vector contr) {
    Enumeration e=contr.elements();
    Struct prem,concl;
    Struct[] implic=new Struct[2];
    int rep_info,rf;
    while (e.hasMoreElements()) {
      implic = (Struct[])e.nextElement();
      verifValid(implic);
    }
  }

  // Supprime une donnée de la BC
  private void delData(Struct[] implic) {
    moi.vectTheory.delImplic(implic[0],implic[1]);
  }

  /** Explique à l'élève un prédicat, pas encore implémenté
   * @return true si le prédicat a été compris de lélève
   * @return false sinon
   */
  public boolean expliquePred(Struct pred) {
    return true;
  }

  private void trace(String msg) {
    if (btrace) System.out.println(moi.getNom()+"> "+msg);
  }

  private void erreur(String msg) {
    System.out.println(moi.getNom()+" erreur> "+msg);
  }

  // vérification de la validité de la formule implic, suppression éventuelle d'implic de la BC
  private void verifValid(Struct[] implic) {
    int rep_info=0;
    trace("Vérification de la validité de \""+VectTheory.getLogicData(implic)+"\".");
    moi.world.sendMsg(lui,9,implic);
    while(rep_info==0) {
      if (buff_mess.size() > 0) {
        if ( ( (Integer) buff_mess.elementAt(0)).intValue() == 4) {
          rep_info = ( (Integer) buff_mess.elementAt(1)).intValue();
          buff_mess.removeElementAt(0);
          buff_mess.removeElementAt(0);
        }
      }
    }
    if (rep_info == 1) {
      trace("Donnée \"" + VectTheory.getLogicData(implic) + "\" fausse donc supprimée.");
      delData(implic);
    }
    else if (rep_info == 2) trace("Donnée \"" + VectTheory.getLogicData(implic) + "\" juste donc conservée.");
    else trace("Donnée \"" + VectTheory.getLogicData(implic) + "\" inconnue du maîte donc conservée.");
  }
}