
import java.io.*;
import java.util.*;
import java.lang.*;

/**
  * @author Ally N, anaaktge@
  * @brief master class to control and run a simulation
  */

public class probControlNetwork {
	
	// instance variables

	// list of nodes who have heard about item
	private ArrayList hasHeard;
	// who will tell their friends in the next iteration of hte graph
	private ArrayList spreadWordNextTurn;
	// total nodes in graph
	private int numNodes;
	// the netowrk 
	public static ArrayList network;
	// number of adopters so far
	private int adopted; 
	// will everyone have the same probability or will it be assigned?
	private boolean isUniformAdoption;
	// if uniform, the universal adoption prob
	private double probAdopt;
	// who the start node is
	public int maven;
	// num of rounds the simulation ran for 
	private int rounds;

	/**
	  * @brief constructor for basic class
	  * @param startMavens nodes who start with product in round 0
	  * @param totalNodes total in graph
	  * @param isUniform false use a random distribution
	  * @param prob prob of acceptance if uniform distr
	  */
	public probControlNetwork(int startMaven,  boolean isUniform,
			double  prob, String networkFile)
	{
		//System.out.println("entering constructor");
		adopted = 0;
		rounds = 0;
		maven = startMaven;
		probAdopt = prob;
		numNodes = -1;
		isUniformAdoption = isUniform;
		createNetwork(networkFile);

		//System.out.println(numNodes);
		
		spreadWordNextTurn = new ArrayList(numNodes);
		
		//System.out.println(((Node)(network.get(maven))).toString());
		
		//((Node)network.get(maven)).distToMaven = 0;
		spreadWordNextTurn.add(network.get(startMaven));
		hasHeard = new ArrayList(numNodes);
	
		//System.out.println("exiting constructor");
	}

	/**
	  * @brief moves the simulation one turn
	  *  check if hasHeard = total, return if so
	  *  advance all 
	  */
	public boolean simulationRound()
	{
		//System.out.println("endtering simulationROund");
		// check hashead = total, simulation over if =, ret T
		if((adopted == numNodes) || (hasHeard.size() == numNodes) ||
				spreadWordNextTurn.isEmpty() )
		{
			return true;
		}
		ArrayList spreadThisTurn = (ArrayList)spreadWordNextTurn.clone();
		// empty spreadWord next turn
		spreadWordNextTurn.clear();
		// for ach node in spreadWord next turn
		for(int i = 0; i< spreadThisTurn.size(); i++)
		{
			Node evanglist = (Node)spreadThisTurn.get(i);
			//for each friend attached to node
			int evanglistNumFriends = evanglist.friends.size();
			for(int j = 0; j< evanglistNumFriends; j++)
				{
					Node aFriend =
						(Node)network.get(((Integer)evanglist.friends.get(j)).intValue());
					if(!(hasHeard.contains(aFriend)))
					{
						if(!(aFriend.isUsing))
						{
							boolean didConvert = aFriend.willAdopt();
							if(didConvert)
							{
								hasHeard.add(aFriend);
								spreadWordNextTurn.add(aFriend);
								adopted++;
							}
						}
						// skip trying to convert if isUsing is true
					}
				}
		} 
		 //System.out.println("exiting simulationROund");
		return false;
	}

	/**
	  * @brief runs sim
	  * @returns int numadopted, dist in links farthest from maven
	  */
	public ArrayList runSimulation()
	{
		 //System.out.println("entering simulationRun");
		// method is missing 
		// while boolean is false
		while(!simulationRound())
		{
			rounds++;
		}
		// return num adopted , farthest as a list
		ArrayList retArray = new ArrayList(2);
		retArray.add(0, adopted);
		retArray.add(1, whichWasFarthest());

		 //System.out.println("exiting simulrun");
		return retArray;
	}

	/**
	  * @brief determines how long farthest from start node _after_ simiulation
	  * looking for dist of longest shortest path to maven
	  */
	public int whichWasFarthest()
	{
		 //System.out.println("entering farthest");
		// walk through list, looking for largest dist to maven
		int mavenReach = ((Node)network.get(0)).distToMaven;
		for (int i = 1; i< numNodes; i++)
		{
			if((((Node)network.get(i)).distToMaven) > mavenReach )
			{
				mavenReach = ((Node)network.get(i)).distToMaven;
			}
		}
		 //System.out.println("exit farthest");
		return mavenReach;
	}

	/**
	  * @brief
	  * @pre space has already been allocated 
	  * @pre assumes no double listing of friendships 
	  * @note, includes file processing, based on
	  * http://www.javacoffeebreak.com/java103/java103.html
	  */
	public void createNetwork(String networkFileName )
	{
		try {
		//System.out.println("inside create network");
		FileInputStream fstream = new FileInputStream(networkFileName);
		DataInputStream in = new DataInputStream(fstream);				              
							              // first line of file is total
		numNodes = Integer.parseInt(in.readLine());
		
		network = new ArrayList(numNodes);

		if(isUniformAdoption)
		{
		//	System.out.println("inside case 1 num nodes ="+ numNodes);
			for(int i=0; i< numNodes; i++)
			{
				Node temp = new Node(probAdopt, numNodes);
				network.add(temp);
				//System.out.println("adding node"+ temp.toString());
				//System.out.println(((Node)network.get(i)).toString());
			}
		}
		else
		{
			for(int i=0; i< numNodes; i++)
			{
				// System.out.println("inside case 2");
				Node temp = new Node (((float)Math.random()), numNodes);
				network.add(temp);
			}
		}
	
		
		while (in.available() !=0)
			{
				// for each line of hte file
				// split on ws into two ints, A, B
				// network.get(A).friends.add(B);
				// network.get(B).friends.add(A);
				String thisLine = in.readLine();
				StringTokenizer tokenizer = new StringTokenizer(thisLine, " ");
				if(tokenizer.countTokens() == 2)
				{
					int friendOne = Integer.parseInt(tokenizer.nextToken());
					int friendTwo = Integer.parseInt(tokenizer.nextToken());
					//debug null ptr comes from here
					//System.out.println("friend 1 and 2 "+friendOne+ " "+
					//		friendTwo );
					((Node)network.get(friendOne)).friends.add(friendTwo);
					((Node)network.get(friendTwo)).friends.add(friendOne);
				}
				else
				{
					System.out.println("EEEEERRROR IN TOKENIZER");
				}
				
			}
			in.close();
			//System.out.println("exiting createnetwork");

		}
		catch (Exception e)
		{
			System.err.println("File input error"+e+ " --");
			e.printStackTrace();
		}
	}




//EOC
}


