/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2006 Phex Development Group
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  Created on 04.01.2006
 *  --- CVS Information ---
 *  $Id: PongCache.java 3732 2007-02-12 00:29:43Z gregork $
 */
package phex.common;

import java.util.*;

import phex.common.collections.PriorityQueue;
import phex.host.HostManager;
import phex.msg.PongMsg;

/**
 * A simple cache of pongs to reduce network bandwidth.
 */
public class PongCache
{    
    private static final int PONGS_PER_HOP = 1;
    private static final int MAX_HOPS = 6;
    
    private static final int EXPIRE_TIME_MILLIS = 60000;
    
    private static PriorityQueue pongQueue;
    
    private PongCache()
    {
        int[] capacities = new int[MAX_HOPS];
        Arrays.fill(capacities, PONGS_PER_HOP);
        pongQueue = new PriorityQueue( capacities );
    }
    
    public static PongCache getInstance()
    {
        return PongCache.Holder.instance;
    }
    
    /*
     * This idiom provides lazy and thread-safe instantiation.
     */
    static private class Holder
    {
        static protected final PongCache instance = new PongCache();
    }

    public List<PongMsg> getPongs( )
    {
        synchronized( pongQueue )
        { 
            List<PongMsg> pongList = new LinkedList<PongMsg>();
            List<PongMsg> removeList = null;
            
            long now = System.currentTimeMillis();
            Iterator iterator = pongQueue.iterator();
            while( iterator.hasNext() )
            {
                PongMsg pong = (PongMsg)iterator.next();
                if( now - pong.getCreationTime() > EXPIRE_TIME_MILLIS )
                {
                    if(removeList == null) 
                    {
                        removeList = new LinkedList<PongMsg>();
                    }
                    removeList.add( pong );
                }
                else
                {
                    pongList.add( pong );
                }
            }
            removePongs( removeList );
            return pongList;
        }
    }

    private void removePongs( List<PongMsg> pongList )
    {
        if ( pongList == null )
        {
            return;
        }
        Iterator<PongMsg> iterator = pongList.iterator();
        while(iterator.hasNext()) 
        {
            PongMsg pong = iterator.next();
            pongQueue.removeFromAll( pong );
        }
    }                             

    public void addPong( PongMsg pong )
    {
        // we only store ultrapeer pongs
        if( !pong.isUltrapeerMarked() )
        {
            return;      
        }
        
        // no caching needed if we are no ultrapeer
        if( !HostManager.getInstance().isUltrapeer() )
        {
            return;
        }

        // reduce by already counted hop..
        int hops = pong.getHeader().getHopsTaken()-1;
        
        // ignore high hops
        if( hops >= MAX_HOPS )   
        {
            return;
        }

        synchronized( pongQueue )
        {
            pongQueue.addToHead( pong, hops );
        }
    }
}