
package org.jgroups.util;


import org.jgroups.Address;

import java.util.*;


/**
 * Contains responses from all members. Marks faulty members.
 * A RspList is a response list used in peer-to-peer protocols. This class is unsynchronized
 */
public class RspList<T extends Object> implements Map<Address,Rsp<T>>, Iterable<Rsp<T>> {
    final Map<Address,Rsp<T>> rsps=new HashMap<Address,Rsp<T>>();


    public RspList() {

    }

    /** Adds a list of responses
     * @param responses Collection<Rsp>
     */
    public RspList(Collection<Rsp<T>> responses) {
        if(responses != null) {
            for(Rsp<T> rsp: responses) {
                rsps.put(rsp.getSender(), rsp);
            }
        }
    }


    public boolean isEmpty() {
        return rsps.isEmpty();
    }

    public boolean containsKey(Object key) {
        return rsps.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return rsps.containsValue(value);
    }

    /**
     * Returns the Rsp associated with address key
     * @param key Address (key)
     * @return Rsp
     */
    public Rsp<T> get(Object key) {
        return rsps.get(key);
    }

    /**
     * Returns the value associated with address key
     * @param key
     * @return Object value
     */
    public T getValue(Object key) {
        Rsp<T> rsp=get(key);
        return rsp != null? rsp.getValue() : null;
    }

    public Rsp<T> put(Address key, Rsp<T> value) {
        return rsps.put(key, value);
    }

    public Rsp<T> remove(Object key) {
        return rsps.remove(key);
    }

    public void putAll(Map<? extends Address, ? extends Rsp<T>> m) {
        rsps.putAll(m);
    }

    public void clear() {
        rsps.clear();
    }

    public Set<Address> keySet() {
        return rsps.keySet();
    }

    public Collection<Rsp<T>> values() {
        return rsps.values();
    }

    public Set<Map.Entry<Address,Rsp<T>>> entrySet() {
        return rsps.entrySet();
    }



    public void addRsp(Address sender, T retval) {
        Rsp<T> rsp=get(sender);
        if(rsp != null) {
            rsp.setValue(retval);
            return;
        }
        rsps.put(sender, new Rsp<T>(sender, retval));
    }


    public void addNotReceived(Address sender) {
        Rsp<T> rsp=get(sender);
        if(rsp == null)
            rsps.put(sender, new Rsp<T>(sender));
    }


    public boolean isReceived(Address sender) {
        Rsp<T> rsp=get(sender);
        return rsp != null && rsp.received;
    }

    public int numSuspectedMembers() {
        int num=0;
        Collection<Rsp<T>> values=values();
        for(Rsp<T> rsp: values) {
            if(rsp.wasSuspected())
                num++;
        }
        return num;
    }

    public int numReceived() {
        int num=0;
        Collection<Rsp<T>> values=values();
        for(Rsp<T> rsp: values) {
            if(rsp.wasReceived())
                num++;
        }
        return num;
    }

    /** Returns the first value in the response set. This is random, but we try to return a non-null value first */
    public T getFirst() {
        Collection<Rsp<T>> values=values();
        for(Rsp<T> rsp: values) {
            if(rsp.getValue() != null)
                return rsp.getValue();
        }
        return null;
    }


    /**
     * Returns the results from non-suspected members that are not null.
     */
    public List<T> getResults() {
        List<T> ret=new ArrayList<T>(size());

        T val;
        for(Rsp<T> rsp: values()) {
            if(rsp.wasReceived() && (val=rsp.getValue()) != null)
                ret.add(val);
        }
        return ret;
    }


    public List<Address> getSuspectedMembers() {
        List<Address> retval=new ArrayList<Address>();
        for(Rsp<T> rsp: values()) {
            if(rsp.wasSuspected())
                retval.add(rsp.getSender());
        }
        return retval;
    }


    public boolean isSuspected(Address sender) {
        Rsp<T> rsp=get(sender);
        return rsp != null && rsp.suspected;
    }


    public int size() {
        return rsps.size();
    }



    public String toString() {
        StringBuilder ret=new StringBuilder();
        for(Rsp<T> rsp: values()) {
            ret.append("[" + rsp + "]\n");
        }
        return ret.toString();
    }


    boolean contains(Address sender) {
        return containsKey(sender);
    }


    public Iterator<Rsp<T>> iterator() {
        return rsps.values().iterator();
    }
}
