package example.simulation;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.util.Filter;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.EqualsFilter;
import com.tangosol.util.filter.GreaterFilter;

import example.entity.*;

public class Driver {

	private Properties props = new Properties();
	private Random random = new Random();
	private NamedCache namedCache = null;
	private ArrayList<String> keys = new ArrayList<String>();
	private int maxTouches = 0;
	
	public int setMaxTouchCount(int target) {
		if (maxTouches < target)
			maxTouches = target;
		return maxTouches;
	}
	
	public ArrayList<String>getKeyList() {
		return keys;
	}
	
	private int getRandomInteger(int aStart, int aEnd){
	    if ( aStart > aEnd ) {
	      throw new IllegalArgumentException("Start cannot exceed End.");
	    }

	    long range = (long)aEnd - (long)aStart + 1;

	    long fraction = (long)(range * random.nextDouble());
	    
	    int randomNumber =  (int)(fraction + aStart);    
	    return randomNumber;
	  }
	  
	
	public Properties getProps() {
		return props;
	}


	
	public void init() throws Exception {
		InputStream fis = null;
		try {

			String configFile = System.getProperty("config.file",
					"./config.properties");
			
		
			if (configFile != null) {
				System.out.println("Running using config property "
						+ configFile);
			}
			try {
				fis = new FileInputStream(configFile); // from current dir
				props.load(fis);
			} catch (Exception e) {

				// Doesn't matter what it is.
			}

			if (fis == null) {
				System.out
						.println("\nconfig.properties not found in this dir. "
								+ "Alternate location can be specified with a -Dconfig.file");
				System.out.println("Using defaults ..");
			}
		} catch (Exception e) {
			throw new IllegalArgumentException("Initialization Failed", e);
		} finally {
			if (fis != null)
				fis.close();
		}
	

		reset();
		addIndices();
		generate();
			
	}
	public int getEntries() {
		return Integer.valueOf(props.getProperty("entries", "3000"));
	}

	public int getRunlength() {
		return Integer.valueOf(props.getProperty("runlength", "60")) * 1000;
	}
	
	/**
	 * Sleep time between actions, in milli-seconds
	 * @return
	 */
	public int getSleepTime() {
		return Integer.valueOf(props.getProperty("sleeptime", "100")) * 1000;
	}
	
	public int displayGenerationInterval() {
		return Integer.valueOf(props.getProperty("display.generation.interval", "0"));
	}

	public String getCacheName() {
		return props.getProperty("cachename", "dist-person");
		
	}
	
	public NamedCache getNamedCache() {
		
		if (namedCache != null) {
			return namedCache;
		}
		return namedCache = CacheFactory.getCache(getCacheName());
	}

	private void reset() {
		if (keys != null)
			keys.clear();
		if ( namedCache != null)
			namedCache.clear();
	}
	
	private void addIndices() {
		System.out.printf("Attempting to add indices\n");
		NamedCache cache = getNamedCache();
		
		cache.addIndex(new ReflectionExtractor("getTouchCount"), false, null);
		
	}
	private void generate() {
		int max = getEntries();
		NamedCache cache = getNamedCache();
		System.out.printf("Generating %d objects\n",max);
		
		for (int i =0; i < max; i++) {
			Person p = generatePerson();
			if ( i % 500 == 0 ) 
				System.out.println("\n++++++++\n"+p.toString() + "\n++++++++\n");
			
			cache.put(p.getKey(), p);
			if ( !keys.contains(p.getKey()))
				keys.add(p.getKey());
			}
	}
	
	private String[] firstNames = {"Jacob","Michael","Ethan","Joshua","Daniel","Emma","Alex","Isabella"};
	private String[] lastNames = {"Brown","Brooks","James","Fowler","Smith","Johnson","Miller","Jones"};
	private String[] streetNames = {"First","Second","Park","Main","Maple","Cedar","Elm","Oak"};
	private String[] streetTypes = {"Street","Road","Rd","Court","Ct","Blvd","Canyon","Meadow"};
	private String[]cities = { "Boston","Burlington","Shirley", "Gloucester","Rockport", "Quincy"};
	
	private String[] sexes = {"male","female"};
	
	private String getRandomString(String[] strings) {
		
		int which = getRandomInteger(0,strings.length-1);
		return strings[which];
	}
	
	private Person generatePerson() {
		
		String name = getRandomString(firstNames) + " " + getRandomString(lastNames);
		Person p = new Person(name,getRandomInteger(35,50),getRandomString(sexes));
		
		String street = new Integer(getRandomInteger(1,5000)).toString() + " " +
				getRandomString(streetNames) + " " + getRandomString(streetTypes);
		
		Address a = new Address(street, getRandomString(cities),"USA");
		p.setAddress(a);
		return p;
	}
	
	public void dumpTouched() {
		System.out.println("Attempting to dump all touched objects");
		
		
		for ( int i =1; i <= maxTouches; i++) { 
		
			Filter filter = new EqualsFilter("getTouchCount",i);
			
			Set set = getNamedCache().entrySet(filter);
			System.out.printf("Found %d objects with touch count %d\n",set.size(),i);
			int count =0;
			for (Iterator people = set.iterator();people.hasNext();) {
				Map.Entry entry = (Map.Entry) people.next();
				count++;
				Person p= (Person)entry.getValue();
				
				if ( count % 100 == 0 || count == 1)
					System.out.println("++++\n" + p.toString() + "\n+++\n");
			}
		
		}
		System.out.println("Dump of all touched objects complete.");
		
	}
	
	public static void main(String[] args) {
		Driver driver = new Driver();
		try {
			driver.init();
			
			Thread.sleep(2000);
	
			System.out.printf("Simulation started\n");
			System.out.printf("Generating %d objects\n",driver.getEntries());
			System.out.printf("Running for %d seconds\n",driver.getRunlength()/1000);
			
			
			
			Simulation runner = new Simulation (driver);
			
			new Thread(runner).start();
			Thread.sleep(driver.getRunlength() + 5000);
			
			driver.dumpTouched();
			
			CacheFactory.shutdown();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
	}
	
}
