/*
 * Created on Oct 19, 2003
 */
package Cluedo.g6;

import java.io.PrintWriter;
import java.util.*;
import Cluedo.Cluedo;

/**
 * @author Vladislav
 */
public class ClueLogic {
	private Cluedo mClue;
	private Disjunct[] players; // indexed from 0 to p inclusive
	private ArrayList clauses[];

	private int[] possibleHolders;
	private int mP;
	private int mC;
	private int mN;
	private int mH;
	private int hiddenPlayer; // the value of p, the hidden player index
	private Random rg;
	private boolean dirty;
	//private PrintWriter log;
	
	public ClueLogic(Cluedo iClue) throws Exception {
		this.rg = new Random(System.currentTimeMillis());
		mClue = iClue;
		mP = mClue.numPlayers();
		mC = mClue.numCardsPerPlayer();
		mN = mClue.numCards();
		mH = mClue.numHiddenCards();

		players = new Disjunct[mP + 1];
		clauses = new ArrayList[mP + 1];
		possibleHolders = new int[mN + 1];

		// the hidden player, is the one with the highest index
		hiddenPlayer = mP;

		for (int i = 0; i <= mP; i++) {
			players[i] = new Disjunct(mN);
			clauses[i] = new ArrayList();
		}

		for (int i = 1; i <= mN; i++) {
			possibleHolders[i] = mP + 1; // account for extra "hidden" player
		}
		
	}



	public void assertHasCard(int player, int card) {
		for (int i = 0; i <= mP; i++) {
			if (i != player) {
				assertLacksCard(i, card);
			}
		}
	}

	public void assertHasCards(int player, int[] cards) {
		for (int i = 0; i < cards.length; i++) {
			assertHasCard(player, cards[i]);
		}
	}

	public void assertLacksCard(int player, int card) {
		// only if you have it in your disjunct...
		if (players[player].get(card)) {
			dirty = true;
			players[player].set(card, false);
			possibleHolders[card]--;

			// fix up my clauses, remove this element from every
			// list
			for (int i = 0; i < clauses[player].size(); i++) {
				if (getClause(player, i).get(card)) {
					//remove that card
					 getClause(player, i).set(card, false);
					if (getClause(player, i).getNumSet() == 0) {
						//Group6Player0.log.print(">> LC: removed clause " + i + " from " + player + "\n");
						clauses[player].remove(i);
						i--;
					}
				}
			}
			
			// IF YOU KNOW SOMONE HAS IT
			// find out who, and remove any clause that has that
			// card in it
			if (possibleHolders[card] == 1) {
				for (int i = 0; i <= mP; i++) {
					if (players[i].get(card)) {
						//go through each of his clauses
						for (int j = 0; j < clauses[i].size(); j++) {
							if (getClause(i, j).get(card)) {
								//remove that whole set
								//Group6Player0.log.print(">> LC: removed clause " + j + " from " + i + "\n");
								clauses[i].remove(j);
								j--;
							}
						}
					}
				}
			}
			
			// should the above be here?
		}
	}

	public void assertLacksCards(int player, int[] cards) {
		for (int i = 0; i < cards.length; i++) {
			assertLacksCard(player, cards[i]);
		}
	}

	public void assertClause(int player, int[] cards) {

		Disjunct tmp = new Disjunct(mN);
		tmp.set(tmp.getIndexList(), false);
		for (int i = 0; i < cards.length; i++)
			tmp.set(cards[i], true);
		clauses[player].add(tmp);

		// clauses[player].add(new Disjunct(mN, cards));
	}

	/**
	 *  
	 */
	public int[] possibleCards(int player) {
		return players[player].getIndexList();
	}

	/**
	 * random cards that don't belong to this player
	 */
	public int[] randomCertainlyNotCards(int player) {
		int cert[] = new int[mN];
		int num = 0;
		for (int i = 1; i < mN + 1; i++) {
			if (possibleHolders[i] <= 1 && !players[player].get(i)) {
				cert[num++] = i;
			}
		}
		//shuffle
		for (int i = 0; i < num; i++) {
			int r = rg.nextInt(num);
			int tmp = cert[i];
			cert[i] = cert[r];
			cert[r] = tmp;
		}

		int len;
		len = rg.nextInt(num);
		len = rg.nextInt(num);	//  because Yaniv is paranoid :)
		len = rg.nextInt(num);
		int[] array = new int[len + 1];
		System.arraycopy(cert, 0, array, 0, len + 1);
		return array;
	}

	/**
	 * @returns cards that me and somone else might have
	 */
	public int[] uncertainCards(int player) {
		int[] possible = possibleCards(player);
		int[] uncertain = new int[possible.length];
		int num = 0;

		for (int i = 0; i < possible.length; i++) {
			if (possibleHolders[possible[i]] > 1)
				uncertain[num++] = possible[i];
		}

		int[] array = new int[num];
		System.arraycopy(uncertain, 0, array, 0, num);
		return array;
	}
	
	/**
	 * @param player
	 * @return the cards that are certainly this player's
	 */
	public int[] certainCards(int player) {
		int[] possible = possibleCards(player);
		int[] certain = new int[possible.length];
		int num = 0;

		for (int i = 0; i < possible.length; i++) {
			if (possibleHolders[possible[i]] == 1)
				certain[num++] = possible[i];
		}

		int[] array = new int[num];
		System.arraycopy(certain, 0, array, 0, num);
		return array;
	}

	/**
	 * @param player
	 * @return returns all the cards in the uncertain list except a few that
	 *         you don't have to ask for b/c you are guranteed an answer
	 *         without them
	 */
	public int[] mostUncertainCards(int player) {
		int[] uncertain = uncertainCards(player);
		int canTakeOut = mC - numCertainCards(player) - 1;
				
		int result[] = new int[uncertain.length - canTakeOut];
		System.arraycopy(uncertain,	0, result, 0, uncertain.length - canTakeOut);
		return result;
	}

	public int numPossibleCards(int player) {
		return players[player].getNumSet();
	}

	public int numCertainCards(int player) {
		return numPossibleCards(player) - uncertainCards(player).length;
	}

	public int maxCards(int player) {
		if (player == hiddenPlayer) {
			return mH;
		} else {
			return mC;
		}
	}



	public void analyzeClauses() {
		//Group6Player0.log.print(this);

		do {
			dirty = false;
			// for each player
			// see what can be inferred from clauses
			// a) if any clause has one item
			// b) if numLeft == 1 then find intersection of all clauses, then
			//	  assert lacks of possible - intersection
			// c) if have k disjoint clauses, and looking numLeft == k, then
			//    remove possible minus union of k disjoint clauses

			for (int i = 0; i <= mP; i++) {
				int numLeft = maxCards(i) - numCertainCards(i);

				if (numLeft == 0 && numPossibleCards(i) > maxCards(i)) {
					assertLacksCards(i, uncertainCards(i));
					assertHasCards(i, possibleCards(i));
				}
				//a) loop through the player's clauses and see
				// if any of them have only one card
				for (int c = 0; c < clauses[i].size(); c++) {
					if (getClause(i, c).getNumSet() == 1) {
						//it must be this card,
						assertHasCard(i, getClause(i, c).getIndexList()[0]);
						//Group6Player0.log.print(">> AC(a): asserted player " + i + " has " + getClause(i, c).getIndexList()[0] + "\n");
					}
				}
				cleanClauses(i);
				//b)
				if (numLeft == 1 && clauses[i].size() > 0) {
					//get intersection of all clauses
					int inter[] = ((Disjunct) clauses[i].get(0)).getIndexList();
					for (int c = 1; c < clauses[i].size(); c++) {
						inter =
							intersection(
								inter,
								getClause(i, c).getIndexList());
					}
					int d[] = diff(uncertainCards(i), inter);
					assertLacksCards(i, d);
//					Group6Player0.log.print(">> AC(b): asserted player " + i + " lacks ");
//					for (int m=0; m<d.length; m++) 
//						Group6Player0.log.print(d[m] + ","); 
//					Group6Player0.log.print("cards\n");
				}

				//c) disjointed

				//clean up my clauses
			}

		} while (dirty);
	}

	public Disjunct getPlayerDisjunct(int playerIndex) {
		return players[playerIndex];
	}

	public Disjunct getHiddenPlayerDisjunct() {
		return players[hiddenPlayer];
	}

	public int[] getPossibleHiddenCards() {
		return players[hiddenPlayer].getIndexList();
	}

	public static int[] union(int a[], int b[]) {
		int[] array = new int[a.length + b.length];
		System.arraycopy(a, 0, array, 0, a.length);
		System.arraycopy(b, 0, array, a.length, b.length);
		return array;

	}	
	
	public static int[] intersection(int a[], int b[]) {
		//a n b
		int num = 0;
		int[] array = new int[0];
		Arrays.sort(a);
		Arrays.sort(b);
		for (int i = 0; i < a.length; i++)
			if (Arrays.binarySearch(b, a[i]) >= 0) {
				if (Arrays.binarySearch(array, a[i]) < 0) {
					//added to make sure result is unique
					int array2[] = new int[++num];
					System.arraycopy(array, 0, array2, 0, num - 1);
					array2[num - 1] = a[i];
					array = array2;
				}
			}
		return array;
	}

	public static int[] diff(int a[], int b[]) {
		//a-b
		int num = 0;
		int[] array = new int[a.length];
		Arrays.sort(b);
		for (int i = 0; i < a.length; i++)
			if (Arrays.binarySearch(b, a[i]) < 0)
				array[num++] = a[i];
		int[] array2 = new int[num];
		System.arraycopy(array, 0, array2, 0, num);
		return array2;
	}

	/**
	 * goes through the clauses and takes out ones that don't say anything
	 */
	private void cleanClauses(int i) {
		int certain[] = certainCards(i);
		for (int c = 0; c < clauses[i].size() - 1; c++) {
			//ook for bigger supersets to delete
			for (int d = c + 1; d < clauses[i].size(); d++) {
				int inter[] =
					intersection(
						getClause(i, c).getIndexList(),
						getClause(i, d).getIndexList());
				if (inter.length
					== getClause(i, c).getNumSet()) {
					clauses[i].remove(d);
					//Group6Player0.log.print(">> CC: removed clause " + d + " from " + i + "\n");
					dirty = true;
					d--;
				} else if (
					inter.length
						== getClause(i, d).getNumSet()) {
					clauses[i].remove(c);
					//Group6Player0.log.print(">> CC: removed clause " + c + " from " + i + "\n");
					dirty = true;
					c--;
					break;
				}
			}
		}
		for (int c = 0; c < clauses[i].size(); c++) {
			if (intersection(getClause(i, c).getIndexList(),certain).length > 0) {
				clauses[i].remove(c);
				c--;
			}
		}
	}

	public String toString() {
		String s = "logic\n";

		for (int i = 0; i <= mP; i++) {
			s += "(" + i + ") ";
			int[] temp = certainCards(i);
			for (int j = 0; j < temp.length; j++)
				s += temp[j] + ", ";
			s += "|";
			temp = uncertainCards(i);
			for (int j = 0; j < temp.length; j++)
				s += temp[j] + ", ";

			s += "\nClauses:\n";
			for (int j = 0; j < clauses[i].size(); j++) {
				s += " - ";
				int nset = getClause(i, j).getNumSet();
				int tmp[] = getClause(i, j).getIndexList();
				for (int k = 0; k < nset; k++) {
					s += tmp[k] + ".";
				}
				s += "\n";
			}

		}

		return s;
	}
	
	private Disjunct getClause(int player, int clause) {
		return (Disjunct) clauses[player].get(clause);
	}

}
