package qms.group;
import java.util.*;
import qms.messages.*;
import qms.util.*;
/**
 * Una lista particolare che permette di gestire l'ordinamento dei messaggi di coordinamento fra i nodi su di una coda
 * @author  Enrico
 */
public class CoordinationMessagesStack implements Comparator {
    protected List stack;
    protected Group     nodes;
    
    public CoordinationMessagesStack(Group group_knowledge) {
        nodes = group_knowledge;
        stack = new LinkedList();
    }
    /**
     * Aggiunge un messaggio e forza il riordinamento della lista
     */
    public synchronized void pushMessage(Message m) {
        stack.add(m);
        Collections.sort(stack,this);
    }
    /**
     * Rimuove un messaggio dalla lista
     */
    public synchronized void removeMessage(Message cm) {
        stack.remove(cm);        
    }
    /**
     * Ottiene una fotografia istantanea della lista di messaggi, utile per l'interfaccia di amministrazione
     */
    public synchronized Message[] getSnapshot() {
        Message[] result = new Message[stack.size()];
        for (int i = 0; i < stack.size(); i++) {
            result[i] = (Message) stack.get(i);
        }
        return result;
    }
    /**
     * Si accerta che siano verificate le condizioni per dichiarare il lock sulla coda da parte del nodo locale.
     * Controlla che la priorità locale sia maggiore della priorità degli altri possibili proprietari, 
     * vede se ci sono nodes.size() messaggi di "agree" e se ci sono li rimuove e ritorna true, altrimenti ritorna false e non fa nulla.
     *
     */
    public synchronized boolean checkAndAcquireLock(String lockid, Set owners) {
        Node local = Node.getLocalNode();
        int local_prio = local.getPriority();
        
        // vediamo di avere un livello di priorità sufficiente rispetto agli altri nodi che supponiamo essere
        // già proprietari della coda, per evitare di "passare davanti" ai nodi più prioritari
        for (Iterator i = owners.iterator(); i.hasNext(); ) {
            Node n = (Node) i.next();
            if (local_prio < n.getPriority() ) return false;
        }
        
        // se il nostro livello è sufficiente allora contiamo
        // quanto altri nodi ci hanno dato il loro "consenso"
        List agreements = new Vector();
        for (Iterator i = stack.iterator(); i.hasNext(); ) {
            Message m = (Message) i.next();
            if (m.getType().equals("queue_agree_lock")) {
                if (m.getBody().equals(lockid)) {
                    agreements.add(m);
                }
            }
                    // il maggiore c'è per affrontare il problema del ban di un nodo a run-time..
            if (agreements.size() >= nodes.size()) {
                // ok. sono verificate tutte lengthcondizioni per dichiaraci "possessori della coda"
                stack.removeAll(agreements);
                return true;
            }
        }
        return false;
    }
    /**
     * Ricerca tutti i messaggi nella lista ai quali si può rispondere con un AGREE e li marca, cambiandone il "tipo" in "queue_lock_agreed"
     */
    public synchronized List findAgreeableLockMessages(Set owners) {
        List result = new Vector();
        int min_prio = -1;
        
        for (Iterator i = stack.iterator(); i.hasNext(); ) {
            Message m = (Message) i.next();
            if (m.getType().equals("queue_request_lock")) {
                Node competitor = nodes.lookupNode(m.getSender());
                if (competitor == null) throw new AssertionError("Unknown node "+m.getSender());
                if (competitor.getPriority() > min_prio)  {
                    min_prio = competitor.getPriority();
                    boolean ok = true;
                    for (Iterator o = owners.iterator(); ok && o.hasNext(); ) {
                        Node n = (Node) o.next();
                        if (n.getPriority() >= competitor.getPriority()) {
                            ok = false;
                        }
                    }
                    if (ok) {
                        // aggiungiamo il messaggio fra quelli da essere "agreed"
                        result.add(m);
                        // e gli cambiamo "tipo"
                        m.setType("queue_lock_agreed");
                    }
                }
            }
        }
        return result;
    }
    /**
     * Rimuove l'indicazione di un lock
     */
    public synchronized Node removeLockID(String lockid) {
        for (Iterator i = stack.iterator(); i.hasNext();) {
            Message m = (Message) i.next();
            if ((m.getType().equals("queue_lock_agreed")) && (m.getBody().equals(lockid))) {
                Node n = nodes.lookupNode(m.getSender());
                i.remove();
                return n;
            }
        }
        // attenzione !!!!!
        Logger.log("Attenzione!!! Qualcuno mi ha chiesto di rimuovere li lockid "+lockid+" che non è mai stato accettato!!!");
        return null;
    }
    /**
     * Funzione che impone l'ordinamento all'interno della lista dei messaggi 
     */
    public int compare(Object o, Object o2) {
        Message m1 = (Message) o;
        Message m2 = (Message) o2;
        
        // da rifare bene !!!
        Node n1 = nodes.lookupNode(m1.getSender());
        Node n2 = nodes.lookupNode(m2.getSender());
        
        int prio = n1.compareTo(n2);
        if (prio == 0) return (int) (m1.getCreationTimeStamp()-m2.getCreationTimeStamp());
        
        return prio;
    }
    
    /**
     * Usato per iterare all'interno dello stack
     */
    public Iterator iterator() { return stack.iterator(); }
    /**
     * Elimina tutti i messaggi per/da un nodo
     */
    public synchronized void removeAllMessagesByNode(Node n) {
        for (Iterator m = stack.iterator(); m.hasNext(); ) {
                Message msg = (Message) m.next();
                if (msg.getSender().equals(n.getName()) ||msg.getRecipient().equals(n.getName()) ) {
                    m.remove();
                }            
        }
    }
}
