import java.util.Date;
import java.util.Random;
import java.lang.Math;
import java.util.Calendar;
import java.util.Vector;

/**
 * Military Group class
 *
 * @author Mike Kemp
 * @author Joe Lau
 * @author Brendan Shaughnessy
 * @author Dr. J
 */
/**
 * Jon Havstad
 * Jesse Eyerman
 */


public class MilitaryGroup extends Group {

    /**
     * Constructor Method
     */
    public MilitaryGroup(char s)
    {
	super();
	type = 'm';
	side = s;
    }

/** Military vs. Military Combat
    att: attacker
    def: defender = recipient of the method call
    env: location   (might be replaced with code in function to check env)
    returns 0 if  no Stack is eliminated
            1 if rebel Stack is eliminated
            2 if imperial Stack is eliminated  */
public int MilitaryCombat(MilitaryGroup attacker) {

	// Check for environ
	char type = (char)environ.getType();

	Vector attEliminated = new Vector();
        Vector defEliminated = new Vector();

	Random dieCast = new Random();
	int attLeaderShipRating = 0;
	int defLeaderShipRating = 0;

	int columnShifts = 0;
	int gcdOfEach;

	int attDie;
	int defDie;

	int indexCount;
	Unit curUnit_a=null;

	int d, a, j, k;
	Unit curUnit_d;

	int attUnitsEliminated;
	int defUnitsEliminated;

	int randomIndex_a=0;
	int randomIndex_d=0;

	int postGCD_a;
	int postGCD_d;

	// Search for Leader
	attLeaderShipRating = 1;
	defLeaderShipRating = 1;
		// Joe's function call will go here

	if(attacker.TotalCombatStrength() < 1 || TotalCombatStrength() < 1) {
		return -1;
	} 

	if(attLeaderShipRating > defLeaderShipRating) {
		columnShifts = attLeaderShipRating - defLeaderShipRating;
	} else
	if(attLeaderShipRating < defLeaderShipRating) {
		columnShifts = -(defLeaderShipRating - attLeaderShipRating);
	}

	// Calculate Combat Strength for each side and find the
	// gcd ratio

	gcdOfEach = gcd(TotalCombatStrength(), attacker.TotalCombatStrength());


	if(gcdOfEach == 0) { gcdOfEach++; }

	postGCD_a = attacker.TotalCombatStrength() / gcdOfEach;
	postGCD_d = TotalCombatStrength() / gcdOfEach;

	// Check for Special Environ
	if(environ.getType() == 'W' || environ.getType() == 'A' 
	   || environ.getType() == 'I' || environ.getType() == 'F'){
		if(attacker.side == 'i') {
			columnShifts--;
		} else {
			columnShifts++;
		}
	}

	Date epoch = new Date();
	dieCast.setSeed(epoch.getTime());
		// How to get just the seconds since UNIX epoch?

	attDie = dieCast.nextInt(5);
	defDie = dieCast.nextInt(5);

	if(postGCD_a+columnShifts >= 11) { postGCD_a = 10; }
	if(postGCD_d+columnShifts >= 11) { postGCD_d = 10; }

	// Calculate loser and winner
	if(postGCD_a - postGCD_d + 5 + columnShifts <= 10 && postGCD_a - postGCD_d + 5 + columnShifts > -1) {
		attUnitsEliminated = attackerCombatStrength[attDie][postGCD_a - postGCD_d+columnShifts + 5];
	} else {
		attUnitsEliminated = attackerCombatStrength[attDie][10];
	}

	if(postGCD_d - postGCD_a + 5 + columnShifts <= 10 && postGCD_d - postGCD_a + 5 + columnShifts > 0) {
		defUnitsEliminated = defenderCombatStrength[defDie][postGCD_d - postGCD_a+columnShifts + 5];
	} else {
		defUnitsEliminated = defenderCombatStrength[defDie][10];
	}

	a = attUnitsEliminated;
	d = defUnitsEliminated;

	j = k = 0;


	while(attacker.units.size() > 0 && a > 0) {

		randomIndex_a = dieCast.nextInt(attacker.units.size());
		curUnit_a = (Unit)attacker.units.elementAt(randomIndex_a);
		attacker.units.remove(randomIndex_a);

		curUnit_a.combat += -1;


		if(curUnit_a.combat == 0) {
			attEliminated.add(curUnit_a);
		} else {
			attacker.units.add(randomIndex_a, curUnit_a);
		}
		a--;
		if(attacker.units.size() == 0) {
			if(attacker.side == 'i')
				return 2;
			else
				return 1;			
		}
	}


	while(units.size() > 0 && d > 0) {

		randomIndex_d = dieCast.nextInt(units.size());
		curUnit_d = (Unit)units.elementAt(randomIndex_d);
			units.remove(randomIndex_d);

		curUnit_d.combat += -1;

		if(curUnit_d.combat == 0) {
			defEliminated.add(curUnit_d);
		} else {
			units.add(randomIndex_d, curUnit_d);
		}

		d--;
	}

	if(attacker.units.size() == 0) {
		if(attacker.side == 'i')
			return 2;
		else
			return 1;
	} else
	if(units.size() == 0) {
		if(attacker.side == 'i')
			return 1;
		else
			return 2;
	}


	// Allocate victory points if any
		// Eliminating any Imperial charachter
		// Eliminating any Imperial Sovereign
return 0;
}

/**
	Sums up the total strength of the combat units in the
	military strength unit */
public int TotalCombatStrength() {

	int totalStrength = 0;
	int indexCount = 0;

	Unit curUnit;

	for(indexCount = 0; indexCount < units.size(); indexCount++ ){
		curUnit = ((Unit)units.get(indexCount));
	//	if(curUnit.getGroupType() == 'm') {
			totalStrength += curUnit.getCombat();
	//	} // If
	} // For

	return totalStrength;
}

/** Checks if a Stack is empty
    Possibly replaced with code in function */
private boolean CheckStackEmpty()
{
	if(units.size() == 0){
		return false;
	} else {
		return true;
	}
}

/** Moves eliminated units to the Underworld and modifies Stack */
public void Janitor(int index)
{
     // frees memory for specified index, modifies Stack
}

/** Checks for eliminated units, moves them to Underworld, modifies Stack */
private void Janitor()
{
     // checks for dead units, frees them, modifies stack
}

private static int gcd(int a, int b) {
	if(b == 0) {
		return a;
	} else {
		return gcd(b, a % b);
	} // If
}
// The first element are the Combat odds and the second is
// the roll of the die.
private int[][] attackerCombatStrength =
		{
			{8, 7, 6, 5, 5, 4, 3, 3, 2, 2, 1},
			{7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1},
			{7, 5, 5, 4, 3, 2, 2, 2, 1, 1, 1},
			{6, 5, 4, 3, 3, 2, 1, 1, 1, 0, 0},
			{5, 4, 4, 3, 2, 1, 1, 0, 0, 0, 0},
			{4, 4, 3, 3, 1, 0, 0, 0, 0, 0, 0}
		}; 
private int[][] defenderCombatStrength =
		{
			{0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 4},
			{0, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5},
			{0, 0, 0, 0, 1, 1, 2, 3, 4, 4, 5},
			{0, 0, 1, 1, 1, 2, 3, 3, 4, 5, 6},
			{1, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7},
			{1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8}
		};

/** Finds column shift for combat table
    att: attacker
    def: defender
    env: location
    returns appropriate shift */
private int CalcAdvantage(MilitaryGroup att, Environ env)
{
     // finds advantages for environs against rebel attributes
     // finds disadvantages for environs against imp. attributes
     // calculates difference between leader strengths
     // returns integer value for column shift
   return 0;
}

/** PDB vs Military Combat
    returns 0 if Military stack survives
            1 if Military stack eliminated
 */
public int RakeAttack(int PDB_level)
{
     // calculates military losses due to rake attack
     // Janitor
     // check if military stack eliminated (returns 1 if true, else 0)

	Random dieCast = new Random();
	int defenderLossDie, a, randomIndex;
	
	int defenderLoss;

	Unit curUnit_d;
	Vector eliminated = new Vector();

	dieCast.setSeed(89072938);

	if(PDB_level == 0) {
		return 0;
	} else
	if(PDB_level == 2) {
		defenderLossDie = dieCast.nextInt(5);
		defenderLoss = defenderCombatStrength[defenderLossDie][8];

		a = defenderLoss;

		while(units.size() != 0 && a > 0) {
			randomIndex = dieCast.nextInt(units.size() - 1);
			curUnit_d = (Unit)units.elementAt(randomIndex);

			curUnit_d.combat += -1;
			if(curUnit_d.combat == 0) {
				eliminated.add(curUnit_d);
				units.remove(randomIndex);
			}
			a--;
		}
	} else
	if(PDB_level == 1) {
		defenderLossDie = dieCast.nextInt(5);
		defenderLoss = defenderCombatStrength[defenderLossDie][6];

		a = defenderLoss;

		while(units.size() != 0 && a > 0) {
			randomIndex = dieCast.nextInt(units.size() - 1);
			curUnit_d = (Unit)units.elementAt(randomIndex);

			curUnit_d.combat += -1;
			if(curUnit_d.combat == 0) {
				eliminated.add(curUnit_d);
				units.remove(randomIndex);
			}
			a--;
		}
		
	}

   return 0;
}

public static void milTest() {

	MilitaryGroup att = new MilitaryGroup('i');
	MilitaryGroup def = new MilitaryGroup('r');

	int i, j, k;
	Unit curUnit;
 
	String[] GracelandW = {"Graceland_W"};

	Environ Graceland =  new Environ(5511,'W',4,GracelandW,5,true,"Wyths",0);

	att.setEnviron(Graceland);
	def.setEnviron(Graceland);

	Unit at1 = new Unit('i', "Atomic Sisters", 10, Graceland, att);
	Unit at2 = new Unit('i', "Atomic Militia", 5, Graceland, att);
	Unit at3 = new Unit('i', "Atomic Veteran", 7, Graceland, att);

	att.add(at1);
	att.add(at2);
	att.add(at3);

	Unit de1 = new Unit('r', "Pygmie Queens", 1, Graceland, def);
	Unit de2 = new Unit('r', "Pygmie Urban", 5, Graceland, def);
	Unit de3 = new Unit('r', "Pygmie Line", 7, Graceland, def);

	def.add(de1);
	def.add(de2);
	def.add(de3);

	k = j = 0;
	do {
		System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
		System.out.println("Before:");
		for(i =0 ; i < def.units.size(); i++) { curUnit = (Unit)def.units.elementAt(i); System.out.println(curUnit.getName() + ":" + curUnit.combat); }
		for(i =0 ; i < att.units.size(); i++) { curUnit = (Unit)att.units.elementAt(i); System.out.println(curUnit.getName() + ":" + curUnit.combat); }
		j = att.MilitaryCombat(def);
		System.out.println("\nAfter:");
		for(i =0 ; i < def.units.size(); i++) { curUnit = (Unit)def.units.elementAt(i); System.out.println(curUnit.getName() + ":" + curUnit.combat); }
		for(i =0 ; i < att.units.size(); i++) { curUnit = (Unit)att.units.elementAt(i); System.out.println(curUnit.getName() + ":" + curUnit.combat); }
		k++;
	} while (k < 100 && j != -1 && (att.TotalCombatStrength() != 0 && def.TotalCombatStrength() 
!= 0));

	System.out.println("\nTotal Fights: " + k);
}//method

public static void main(String args[]) {

	milTest();

return;
}
} // Class
