package profiles;

import java.util.Vector;

/**
 * E' il componente per lo scambio dei messaggi. E' un'oggetto attivo che si
 * basa sullo strato sottostante implementato da BasicService.
 *
 * @author Lorenzo Moretti
 * @version 1.0
 */

public class ProfileMatcher extends Thread {
  Device thisDevice;
  BasicService bs;
  Profile thisProfile;
  long sleepTime = 5000;
  long reqTimeout = 20000;
  long elapsedTimeout = 0;
  boolean keepRunning = true;
  boolean isElected = false;
  Vector profiles = new Vector();

  public ProfileMatcher(Device device, BasicService basicService, Profile profile) {
    thisDevice = device;
    bs = basicService;
    thisProfile = profile;
  }

  /**
   * Da chiamare tramite start(). E' il ciclo di ricerca tra i profili per
   * trovare quelli che fanno match.
   */
  public void run() {
    keepRunning = true;
    isElected = false;
    int counter = 0;

    profiles.addElement(thisProfile);
    while (keepRunning) {
      counter++;

      // Ogni quanto rimuovere il i profili vecchi
      if (counter % 5 == 0) {
        profiles.removeAllElements();
        profiles.addElement(thisProfile);
      }

      if (isElected || elapsedTimeout > reqTimeout) {
        isElected = true;

        System.out.println("Mi ritengo attualmente eletto!");

        // Invio richiesta bcast
        TextMessage msg = new TextMessage("PREQ",
                                          thisDevice.getId(),
                                          Message.BCAST,
                                          2);
        // Cambiando TTL il gestore
        // aumenta la propia sfera di influenza
        bs.bcast(thisDevice, msg);

        // Esegui tutti i confronti
        for (int i = 0; i < profiles.size(); i++) {
          Profile pi = (Profile)profiles.elementAt(i);
          for (int j = i+1; j < profiles.size(); j++) {
            Profile pj = (Profile)profiles.elementAt(j);
            if (pj.match(pi) && !pi.getOwner().equals(pj.getOwner())) {
              // Segnala i match
              String t = ("Trovata corrispondenza tra " +
                          pi.getOwner() + " e " + pj.getOwner());

              // Notifica il match sullo schermo
              javax.microedition.lcdui.Alert alert =
                  new javax.microedition.lcdui.Alert("Match!");
              alert.setString(t);
              alert.setType(javax.microedition.lcdui.AlertType.INFO);
              alert.setTimeout(2000);
              javax.microedition.lcdui.Display.getDisplay(profilesj2me.ProfilesJ2ME.getInstance())
                  .setCurrent(alert);

              /*
              // Evita di inviarti un messaggio!
              if (!thisDevice.getId().equals(pi.getOwner())) {
                Vector r = thisDevice.getRoutingTable().getRouteTo(pi.getOwner());
                if (r.size() > 0) {
                  TextMessage msg1 = new TextMessage(t, thisDevice.getId(),
                      Message.USR_MSG);
                  msg1.setRoute(r);
                  bs.deliverMessage(msg1);
                }
              } else {
                // Notifica il match sullo schermo
                javax.microedition.lcdui.Alert alert =
                    new javax.microedition.lcdui.Alert("Match!");
                alert.setString(t);
                alert.setType(javax.microedition.lcdui.AlertType.INFO);
                alert.setTimeout(2000);
                javax.microedition.lcdui.Display.getDisplay(profilesj2me.ProfilesJ2ME.getInstance())
                    .setCurrent(alert);
              }


              // Evita di inviarti un messaggio!
              if (!thisDevice.getId().equals(pj.getOwner())) {
                Vector r = thisDevice.getRoutingTable().getRouteTo(pj.getOwner());
                if (r.size() > 0) {
                  TextMessage msg1 = new TextMessage(t, thisDevice.getId(),
                      Message.USR_MSG);
                  msg1.setRoute(r);
                  bs.deliverMessage(msg1);
                } else {
                  // Notifica il match sullo schermo
                  javax.microedition.lcdui.Alert alert =
                      new javax.microedition.lcdui.Alert("Match!");
                  alert.setString(t);
                  alert.setType(javax.microedition.lcdui.AlertType.INFO);
                  alert.setTimeout(2000);
                  javax.microedition.lcdui.Display.getDisplay(profilesj2me.ProfilesJ2ME.getInstance())
                      .setCurrent(alert);
                }
              }
              */
            }
          }
        }



      }
      try {
        sleep(sleepTime);
      }
      catch (InterruptedException ex) {
      }
      elapsedTimeout += sleepTime;

    }
  }

  /**
   * Notifica di richiesta di messaggio. Al richiedente viene inviato il profilo
   * se l'oggetto non è al momento anch'esso gestore dei profili. In tal caso
   * l'oggetto attuale non si considera più gestore se ha un id più basso del
   * gestore che ha effettuato la richiesta.
   * @param requestor id del richiedente
   */
  public void profileRequested(String requestor) {
    if (!isElected || requestor.compareTo(thisDevice.getId()) < 0) {
      isElected = false;
      elapsedTimeout = 0;
      TextMessage msg = new TextMessage(thisProfile.toString(),
                                        thisDevice.getId(),
                                        Message.USR_MSG);
      Vector route = new Vector();
      route.addElement(requestor);
      msg.setRoute(route);
      System.out.println("Prova ad inviare il mio profilo");
      bs.deliverMessage(msg);
      System.out.println("Inviato profilo " + msg);
    }
  }


  /**
   * Notifica di quando viene ricevuto un nuovo profilo. Il nuovo profilo viene
   * aggiunto nell'elenco dei profili su cui effettuare i controlli di
   * corrispondenza.
   */
  public void addProfile(Profile p) {
    System.out.println("Ricevuto profilo: " + p);
    for (int i = 0; i < profiles.size(); i++) {
      if (((Profile)profiles.elementAt(i)).getOwner().equals(p.getOwner()))
        profiles.removeElementAt(i);
    }
    profiles.addElement(p);
  }

  /**
   * Indica se il componente è al momento un gestore dei profili.
   * @return true se è gestore.
   */
  public boolean isElected() {
    return isElected;
  }

  /**
   * Tempo di attesa prima di rieffettuare il controllo sui profili.
   * @return il tempo di attesa.
   */
  public long getSleepTime() {
    return sleepTime;
  }

  /**
   * Tempo di attesa prima di rieffettuare il controllo sui profili.
   * @param sleepTime
   */
  public void setSleepTime(long sleepTime) {
    this.sleepTime = sleepTime;
  }

  /**
   * Ferma l'esecuzione.
   */
  public void stopRunning() {
    keepRunning = false;
  }
}