package DialML;

import alice.tuprolog.*;

import java.util.*;

public class AgentCog extends Thread {

  // mon nom
  private String name;

  // le monde des agents dans lequel je vie
  public World world;

  // mon éventuel maître
  public AgentCog master;

  // mon éventuel élève
  private AgentCog student;

  // mon interlocuteur (soit le maître, soit l'élève)
  private AgentCog lui;

  // Machine Prolog
  private Prolog engine;
  // Connaisances format prolog
  private Theory kb;
  // Résultat d'une requète prolog
  private SolveInfo result;
  // Connaissances format général
  public VectTheory vectTheory;

  // explique-je ce que je fait ?
  private boolean btrace;

  // l'éventuelle leçon que je doit eneigner
  private Vector lesson;

  // toutes mes stratégies disponibles
  public Strategies strat;

  // rôles fonctionnels à disposition des agents
  public RoleFctl rfctl;

  // Naissance de l'agent
  AgentCog(String n,World w) {
    master=null;
    student=null;
    lui=null;
    world=w;
    name=new String(n);
    engine=new Prolog();
    vectTheory=new VectTheory();
    btrace=true;
    lesson=new Vector();
  }

  // Ce qu'il faut enseigner
  public void addImplicToTeach(Struct prem, Struct concl) {
    Struct[] implic=new Struct[2];
    implic[0]=(Struct)prem.copy();
    implic[1]=(Struct)concl.copy();
    if (!lesson.contains(implic)) {
      if (strat.getPathImplic(implic).size()!=0)
        lesson.addElement(implic);
      else
        erreur("Impossible d'enseigner l'implication \""+
               VectTheory.getLogicImplic(implic[0],implic[1])+
               "\" car elle est inconnue de la base.");
    }
  }
  public void addFactToTeach(Struct fact) {
    Struct[] implic=new Struct[2];
    implic[0]=new Struct("");
    implic[1]=(Struct)fact.copy();
//    if (!lesson.contains(implic)) {
//      if (vectTheory.contains(implic) ||
//          strat.getPathImplic(implic).size()!=0)
        lesson.addElement(implic);
//      else
//        erreur("Impossible d'enseigner le fait \""+
//               VectTheory.getLogicFact(fact)+
//               "\" car il est inconnu de la base.");
//    }
  }

  // Qui qui c'est mon étudiant
  public void setStudent(AgentCog ac) {
    student=ac;
    lui=ac;
    strat=new Strategies(this,student);
    rfctl=new RoleFctl(this,student);
    strat.setRfctl(rfctl);
  }

  // Qui qui c'est mon maître
  public void setMaster(AgentCog ac) {
    master=ac;
    lui = ac;
    strat = new Strategies(this, master);
    rfctl = new RoleFctl(this, master);
    strat.setRfctl(rfctl);
  }

  public String getNom() {
    return name;
  }

  public void learnImplic(Struct prem, Struct concl) {
    if (canAddImplic(prem, concl)) {
      vectTheory.addImplic(prem, concl);
      trace("Implication \""+vectTheory.getLogicImplic(prem,concl)+"\" apprise de "+master.getNom()+".");
    }
  }

  public void learnFact(Struct fact) {
    if (canAddFact(fact)) {
      vectTheory.addFact(fact);
      trace("Fait \""+vectTheory.getLogicFact(fact)+"\" appris de "+master.getNom()+".");
    }
  }

  // récupération d'une question sur un fait
  public String getAnswer(Struct fact) {
    setTheory(vectTheory.toProlog());
    setResult(fact);
    return getSolution();
  }
  // récupération d'une question sur deux faits
  public String getAnswer(Struct fact1,Struct fact2) {
    setTheory(vectTheory.toProlog());
    setResult(fact1,fact2);
    return getSolution();
  }

  private void setTheory(String th) {
    System.out.println(th);
    try {
      kb = new Theory(th);
      engine.setTheory(kb);
    }
    catch(InvalidTheoryException ite) {
      erreur("Théorie mal formée.");
    }
  }

  // Calcul du résultat de la réponse pour un question sur un fait
  private void setResult(Struct fact) {
    System.out.println("****"+VectTheory.getPrologFact(fact)+"\n");
    try {
      result = engine.solve(VectTheory.getPrologFact(fact)+"\n");
    }
    catch(MalformedGoalException mge) {
      erreur("Question malformée.");
    }
  }
  // Calcul du résultat de la réponse pour un question sur une implication
  private void setResult(Struct fact1,Struct fact2) {
    try {
      result = engine.solve(VectTheory.getPrologFact(fact1,fact2)+"\n");
    }
    catch(MalformedGoalException mge) {
      erreur("Question malformée.");
    }
  }

  private String getSolution() {
    Term sol=null;
    Substitution sub=null;
    String res=new String();
    try {
      sol = result.getSolution();
      sub = result.getSubstitution();
    }
    catch(NoSolutionException nse) {
      trace("Pas de solution.");
      return new String();
    }
    return ">"+sol.toString()+"<>"+sub.toString()+"<>"+result.isSuccess();
  }

// Autonomie de l'agent
  public void run() {

    int rf;
    Struct[] implic;
    boolean other_sat=false,ready=false;

    // attente d'initialisation de l'interlocuteur
    while(!ready) {
      if (lui!=null)
        if (!lui.getNom().equals("Créateur"))
          if (lui.strat != null)
            ready = true;
    }
    trace("Ma vie commence maintenant !");

    if (student!=null) {
      trace("Je suis le maître de "+student.getNom()+".");
      strat.teachLesson(lesson);
    }
    else {
      trace("BC avant la leçon :\n"+vectTheory.toLPPO());

      trace(master.getNom()+" sera mon maître.");
      strat.learnLesson();
      trace("BC après la leçon :\n"+vectTheory.toLPPO());
    }
    trace("Ma vie se termine maintenant !");
  }

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

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

  public void getData(int type, Object o) {
    strat.addMessage(type,o);
  }

  // Peut-on ajouter l'implication dans la base ?
  private boolean canAddImplic(Struct prem, Struct concl) {
    return rfctl.canAddKnowledge(prem,concl);
  }

  // Peut-on ajouter le fait dans la base ?
  private boolean canAddFact(Struct fact) {
    return rfctl.canAddKnowledge(new Struct(""),fact);
  }
}
