package org.klomp.snark;

import java.util.HashSet;
import java.util.Set;

/**
 * This class is used solely by PeerCoordinator.
 * Caller must synchronize on many of these methods.
 */
class Piece implements Comparable<Piece> {

    private final int id;
    private final Set<PeerID> peers;
    /** @since 0.8.3 */
    private volatile Set<PeerID> requests;
    /** @since 0.8.1 */
    private int priority;
    
    public Piece(int id) {
        this.id = id;
        this.peers = new HashSet<PeerID>(I2PSnarkUtil.MAX_CONNECTIONS / 2);
        // defer creating requests to save memory
    }
    
    /**
     *  Highest priority first,
     *  then rarest first
     */
    public int compareTo(Piece op) {
        int pdiff = op.priority - this.priority;   // reverse
        if (pdiff != 0)
            return pdiff;
        return this.peers.size() - op.peers.size();
    }
    
    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (o instanceof Piece) {
            return this.id == ((Piece)o).id;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 31 * hash + this.id;
        return hash;
    }
    
    public int getId() { return this.id; }

    /** caller must synchronize */
    public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }

    /**
     * Caller must synchronize.
     * @return true if removed
     */
    public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }

    /**
     * How many peers have this piece?
     * Caller must synchronize
     * @since 0.9.1
     */
    public int getPeerCount() {
        return this.peers.size();
    } 
    
    /** caller must synchronize */
    public boolean isRequested() {
        return this.requests != null && !this.requests.isEmpty();
    }

    /**
     * Since 0.8.3, keep track of who is requesting here,
     * to avoid deadlocks from querying each peer.
     * Caller must synchronize
     */
    public void setRequested(Peer peer, boolean requested) {
        if (requested) {
            if (this.requests == null)
                this.requests = new HashSet<PeerID>(2);
            this.requests.add(peer.getPeerID());
        } else {
            if (this.requests != null)
                this.requests.remove(peer.getPeerID());
        } 
    } 
    
    /**
     * Is peer requesting this piece?
     * Caller must synchronize
     * @since 0.8.3
     */
    public boolean isRequestedBy(Peer peer) {
        return this.requests != null && this.requests.contains(peer.getPeerID());
    } 
    
    /**
     * How many peers are requesting this piece?
     * Caller must synchronize
     * @since 0.8.3
     */
    public int getRequestCount() {
        return this.requests == null ? 0 : this.requests.size();
    } 

    /**
     * Clear all knowledge of peers
     * Caller must synchronize
     * @since 0.9.3
     */
    public void clear() {
        peers.clear();
        if (requests != null)
            requests.clear();
    } 
    
    /** @return default 0 @since 0.8.1 */
    public int getPriority() { return this.priority; }

    /** @since 0.8.1 */
    public void setPriority(int p) { this.priority = p; }

    /** @since 0.8.1 */
    public boolean isDisabled() { return this.priority < 0; }

    /** @since 0.8.1 */
    public void setDisabled() { this.priority = -1; }

    @Override
    public String toString() {
        return String.valueOf(id);
    }
}
