import java.util.*;
 
/********************************************************************* 
 * The District class holds all of our Voters, Candidates, and Reps. 
 * Voters are created during construction of the District.  Candidates 
 * are placed in a Candidate Pool and drawn from the Voters.  The 
 * District is also responsible for conducting votes and electing 
 * Reps to Congress. 
 ********************************************************************/ 
public class District extends Vector { 
    public int policies, numberOfBills, positions, tagLength, termLimit;
    public Pool CandidatePool; 
    public String name = ""; 
    public Rep CurrentRep; 
    public Congress Government; 
    public double maxSeparation; 
    public int[] median; 
    public int medianUtility; 
    public int center; 
    public int SPREAD = 5; 
 
    // Establish the district with all the necessary parameters. 
    // Voters are created here. 
    public District(int numVoters, int policies, int numberOfBills, 
		    int positions, int tagLength, String districtName,  
		    int termLimit) { 
	this.policies = policies; 
	this.positions = positions; 
	this.tagLength = tagLength; 
	this.name = districtName; 
	this.numberOfBills = numberOfBills; 
	this.termLimit = termLimit; 
	center = 1 + Math.abs(Utilities.rand.nextInt() % (positions-2));
	if (center == 1 || center == 7)
	    SPREAD--;
	maxSeparation = Math.sqrt(Math.pow(SPREAD - 1, 2) * policies); 
	for (int i=0; i<numVoters; i++) { 
	    Voter tempVoter = new Voter(policies, positions, tagLength, center, 
					numberOfBills, districtName + " Voter" + i); 
	    tempVoter.parent = this; 
	    addElement(tempVoter); 
	} 
	median = new int[policies]; 
	for (int i=0; i<policies; i++) 
	    median[i] = center; 
        findMedian(); 
	CandidatePool = new Pool(this);
 	// for debugging 
	// System.out.println("Establishing " + name); 
    } 
 
    // Finds the utility for the median ideology
    public void findMedian() { 
	for (int i=0; i<size(); i++) 
	    medianUtility += ((Voter)elementAt(i)).findUtility(median); 
    } 

    // Finds the Centrality of an Individual relative to the median.
    public double findCentrality(Individual person) { 
        int platformUtility = 0; 
        double centrality = 0; 
	int[] platform = person.ideology; 
        for (int i=0; i<size(); i++)  
            platformUtility += ((Voter)elementAt(i)).findUtility(platform); 
        centrality = ((double)medianUtility)/((double)platformUtility); 
        return(centrality); 
    } 
 
    // This method finds the elgible candidates within the district and 
    // places then in the CandidatePool vector for consideration by the voters
    public void FindCandidates() { 
 
	System.out.print(center + "\t"); 

	// When the Representative reaches retirement age, then  
	// replace with a new voter in the district. 
	// Otherwise, add the Current Rep to the candidate pool 
	if (CurrentRep != null) {
	    if (CurrentRep.age >= termLimit) { 
		Voter tempVoter = new Voter(policies, positions,  
					    tagLength, center, numberOfBills, 
					    name + " Voter" + CurrentRep.VoterID); 
		tempVoter.parent = this; 
		setElementAt(tempVoter, CurrentRep.VoterID); 
		CurrentRep = null; 
	    } 
	    else {
		CandidatePool.insertElementAt(new Candidate(CurrentRep), 0); 
	    }
	}

	// 1/15th of Voters are selected randomly.  If the votes received by  
	// the current Rep are high, their chances of running are low.   
	// They only challenge when the incumbent looks weak. 
	for (int i=0; i<size()/15; i++) { 
	     
	    // Get the candidate 
	    int who = Math.abs(Utilities.rand.nextInt() % size()); 
	    Voter possibleCandidate = (Voter)elementAt(who); 
	     
	    // if they're already in the pool, don't place them in again 
	    boolean canWin = true;  
	    if (possibleCandidate.isCandidate) { 
		canWin = false;	     
	    }	     
 
	    // If the incumbent is strong, don't jump into the race. 
	    if (CurrentRep != null) { 
		double percent = (double)CurrentRep.votesReceived / 
		    (double)size(); 
		if (percent > possibleCandidate.ambition) { 
		    canWin = false; 
		} 
	    } 
	     
	    // If your positions are too close, then don't jump into the race 
	    if (CandidatePool.size() != 0) 
		for (int j = 0; j<CandidatePool.size(); j++) { 
		    Candidate otherCand = (Candidate)CandidatePool.elementAt(j); 
		    double separation = possibleCandidate.findSeparation(otherCand.ideology) / maxSeparation; 
		    if (separation < .4) { 
			canWin = false; 
		    } 
		} 
	     
	    // if they can win, place them in the pool 
	    if (canWin) { 
		Candidate tempCand = new Candidate(possibleCandidate, who); 
		CandidatePool.addElement(tempCand); 
		possibleCandidate.isCandidate = true; 
	    } 
	} 
	 
	System.out.print(CandidatePool.size()+"\t"); 
 
	// System.out.println("Showing ideologies for Candidates, 
	//                     size = " + CandidatePool.size()); 
	// for (int i=0; i<CandidatePool.size(); i++) 
	//     ((Candidate)CandidatePool.elementAt(i)).showIdeology(); 
    } 
     
    // Here we find the winner of the election in this district 
    public Candidate ConductVote() { 
	int[] theVote = new int[CandidatePool.size()]; 
	double aveLaziness = 0; 
 
	// Have each Voter vote. 
	for (int i=0; i<size(); i++) { 
	    Voter tempVoter = (Voter)elementAt(i); 
	    theVote[tempVoter.vote(CandidatePool, true)]++; 
	    aveLaziness += tempVoter.laziness; 
	} 
	aveLaziness = aveLaziness / size(); 
	System.out.print(aveLaziness+"\t"); 
	 
	// Find the winner 
	int winnerIndex = 0; 
	((Candidate)CandidatePool.elementAt(0)).votesReceived = theVote[0]; 
	// System.out.print(theVote[0] +" "); 
	for (int i=1; i<CandidatePool.size(); i++) { 
	    ((Candidate)CandidatePool.elementAt(i)).votesReceived = theVote[i]; 
	    // System.out.print(theVote[i] + " "); 
	    if (theVote[i] > theVote[winnerIndex]) 
		winnerIndex = i; 
	} 
	// System.out.println(""); 
 
	System.out.print(theVote[winnerIndex]+"\t"); 
 
	// remove the Winner from the CandidatePool 
	Candidate theWinner = (Candidate)CandidatePool.elementAt(winnerIndex); 
	CandidatePool.removeElementAt(winnerIndex); 
	 
	// we return the winning Candidate to the calling function 
	return theWinner; 
    } 
     
    public Rep ElectRep() { 
 
	// Find the Candidates and conduct a vote 
	FindCandidates(); 
	Rep OurRep = new Rep(ConductVote()); 
 
	// Find the centrality of the winning candidate
	double tempCent = findCentrality(OurRep); 
	System.out.print(tempCent+"\t"); 
 
	if (CurrentRep == null) 
	    System.out.print("2\t"); 
	else if (OurRep.VoterID == CurrentRep.VoterID) 
	    System.out.print("0\t"); 
	else 
	    System.out.print("1\t"); 
 
	// Take the old Rep out of congress & put the new one in 
	if (CurrentRep != null) 
	    ((Voter)elementAt(CurrentRep.VoterID)).inCongress = false; 
	CurrentRep = OurRep; 
	((Voter)elementAt(CurrentRep.VoterID)).inCongress = true; 

	// Losing Candidates trade and steal platforms
	CandidatePool.Adapt();
 	 
	// give the elected Rep to the calling method. 
	return OurRep; 
    }
} 

