/*
 * $Header: IDFactoryTest.java, 26/08/2005 01:04:53 luisantonioa Exp $
 *
 * $Author: luisantonioa $
 * $Date: 26/08/2005 01:04:53 $
 * $Revision: 1 $
 * $Log: IDFactoryTest.java,v $
 * Revision 1  26/08/2005 01:04:53  luisantonioa
 * Added copyright notice
 *
 * 
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */
package com.edelvez.gameserver.idfactory;

import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase; 
import com.edelvez.Config;
import com.edelvez.Server;
import com.edelvez.Config.IdFactoryType;
import com.edelvez.util.Rnd;

/**
 * This class ...
 * 
 * @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
 */
public class IDFactoryTest extends TestCase
{
    private static final boolean _debug = false;
    // Compaction, BitSet, Stack, (null to use config)
    private static final IdFactoryType FORCED_TYPE = IdFactoryType.Stack;
    protected IdFactory _idFactory;
    protected AtomicInteger _count = new AtomicInteger(0),
	    _adds = new AtomicInteger(0), _removes = new AtomicInteger(0);
    protected static final int REQUESTER_THREADS = 50;
    protected static final int REQUESTER_THREAD_REQUESTS = 1000;
    protected static final int REQUESTER_THREAD_RANDOM_DELAY = 30;
    protected static final int RELEASER_THREADS = 50;
    protected static final int RELEASER_THREAD_RELEASES = 1000;
    protected static final int RELEASER_THREAD_RANDOM_DELAY = 35;
    private static final long F_SLEEP_INTERVAL = 100;
    CountDownLatch _latch = new CountDownLatch(REQUESTER_THREADS + RELEASER_THREADS);
    protected static Vector<Integer> _map = new Vector<Integer>();

    public static void main(String[] args)
    {
    }

    /**
     * Constructor for IDFactoryTest.
     * 
     * @param arg0
     */
    public IDFactoryTest(String arg0)
    {
	super(arg0);
	Server.serverMode = Server.MODE_GAMESERVER;
	Config.load();
	if (FORCED_TYPE != null)
	    Config.IDFACTORY_TYPE = FORCED_TYPE;
	_idFactory = IdFactory.getInstance();
    }

    /*
     * @see TestCase#setUp()
     */
    protected void setUp() throws Exception
    {
	super.setUp();
	System.out.println("Initial Free ID's: " + IdFactory.FREE_OBJECT_ID_SIZE);
	System.out.println("IdFactoryType: " + Config.IDFACTORY_TYPE.name());
	/*
	 * long startMemoryUse = getMemoryUse(); BitSet freeIds = new
	 * BitSet(0x6FFFFFFF); long endMemoryUse = getMemoryUse();
	 * freeIds.clear(); long approximateSize = ( endMemoryUse -
	 * startMemoryUse ) / (1024*1024); System.out.println("Size:
	 * "+approximateSize+"Mb.");
	 */
    }

    /*
     * @see TestCase#tearDown()
     */
    protected void tearDown() throws Exception
    {
	super.tearDown();
	_idFactory = null;
    }

    /*
     * Test method for
     * 'com.edelvez.gameserver.idfactory.IdFactory.getNextId()'
     */
    public final void testFactory()
    {
	System.out.println("Free ID's: " + _idFactory.size());
	System.out.println("Used ID's: " + (IdFactory.FREE_OBJECT_ID_SIZE - _idFactory.size()));
	_map.add(_idFactory.getNextId());
	for (int i = 0; i < REQUESTER_THREADS; i++)
	{
	    new Thread(new RequestID(), "Request-Thread-" + i).start();
	}
	for (int i = 0; i < RELEASER_THREADS; i++)
	{
	    new Thread(new ReleaseID(), "Release-Thread-" + i).start();
	}
	try
	{
	    _latch.await();
	} catch (InterruptedException e)
	{
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	System.out.println("Free ID's: " + _idFactory.size());
	System.out.println("Used ID's: " + (IdFactory.FREE_OBJECT_ID_SIZE - _idFactory.size()));
	System.out.println("Count: " + _count.get());
    }

    public class RequestID implements Runnable
    {
	long _time1;
	long _time2;
	AtomicInteger _myCount = new AtomicInteger(0);

	public void run()
	{
	    for (int i = 0; i < REQUESTER_THREAD_REQUESTS; i++)
	    {
		synchronized (_map)
		{
		    _time1 = System.nanoTime();
		    int newId = _idFactory.getNextId();
		    _time2 = System.nanoTime() - _time1;
		    _count.incrementAndGet();
		    _adds.incrementAndGet();
		    _myCount.incrementAndGet();
		    _map.add(newId);
		    if (_debug)
			System.out.println("Got new ID " + newId);
		    if (Rnd.nextInt(10) == 0)
		    {
			System.out.println("					Total ID requests: " + _adds.get() + ". " + _time2 + "ns");
		    }
		}
		try
		{
		    Thread.sleep(Rnd.nextInt(REQUESTER_THREAD_RANDOM_DELAY));
		} catch (InterruptedException e)
		{
		    System.out.println(Thread.currentThread().getName() + " was Interupted.");
		}
	    }
	    if (_debug)
		System.out.println(getName() + " myCount is " + _myCount.get() + "/100.");
	    _latch.countDown();
	}
    }

    public class ReleaseID implements Runnable
    {
	AtomicInteger _myCount = new AtomicInteger(100);
	long _time1;
	long _time2;

	public void run()
	{
	    for (int i = 0; i < RELEASER_THREAD_RELEASES; i++)
	    {
		synchronized (_map)
		{
		    int size = _map.size();
		    if (_map.size() <= 0)
		    {
			i--;
			continue;
		    }
		    // if (size > 0)
		    // {
		    int pos = Rnd.nextInt(size);
		    int id = _map.get(pos);
		    _time1 = System.nanoTime();
		    _idFactory.releaseId(id);
		    _time2 = System.nanoTime() - _time1;
		    _map.remove(pos);
		    _count.decrementAndGet();
		    _myCount.decrementAndGet();
		    _removes.incrementAndGet();
		    if (_debug)
			System.out.println("Released ID " + id);
		    if (Rnd.nextInt(10) == 0)
		    {
			System.out.println("Total ID releases: " + _removes.get() + ". " + _time2 + "ns");
		    }
		    // }
		}
		try
		{
		    Thread.sleep(Rnd.nextInt(RELEASER_THREAD_RANDOM_DELAY));
		} catch (InterruptedException e)
		{
		}
	    }
	    if (_debug)
		System.out.println(getName() + " count is " + _myCount.get() + "/100.");
	    _latch.countDown();
	}
    }

    @SuppressWarnings("unused")
    private static long getMemoryUse()
    {
	putOutTheGarbage();
	long totalMemory = Runtime.getRuntime().totalMemory();
	putOutTheGarbage();
	long freeMemory = Runtime.getRuntime().freeMemory();
	return totalMemory - freeMemory;
    }

    private static void putOutTheGarbage()
    {
	collectGarbage();
	collectGarbage();
    }

    private static void collectGarbage()
    {
	try
	{
	    System.gc();
	    Thread.sleep(F_SLEEP_INTERVAL);
	    System.runFinalization();
	    Thread.sleep(F_SLEEP_INTERVAL);
	} catch (InterruptedException ex)
	{
	    ex.printStackTrace();
	}
    }
}
