/*
 * Created on 01.12.2003
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.operation;

import java.util.*;

/**
 * Storage of a collection of multiple base-operations.
 * Additionally this class has several needed methods to
 * manipulate or generate new OperationQueue's. For Example:<br>
 * - shuffle<br>
 * - get all permutations of the operations the queue holds<br>
 * - execution of the whole cue<br>
 * - load<br>
 * - save<br>
 * 
 * @author Markus Krebs
 */
public class OperationQueue implements Comparable {
	private Vector operations;
	
	private int rating;
	
	/**
	 * Creates a OperationQueue
	 */
	public OperationQueue() {
		operations = new Vector();
		rating = 0; //Integer.MIN_VALUE;
	}
	/**
	 * Adds a Operation via ID
	 * @param id operation-ID
	 */
	public void add(String id) {
		operations.add(new Operation(id));
	}
	/**
	 * Adds a Operation
	 * @param operation Operation
	 */
	public void add(Operation operation) {
		operations.add(operation);
		rating += operation.getRating();
	}
	/**
	 * Adds all Operations of the delivered OperationQueue to this
	 * OperationQueue
	 * @param o OperationQueue
	 */
	public void addAll(OperationQueue o) {
		operations.addAll(o.operations);
		rating += o.getRating();
	}
	/**
	 * Gets the index'th Operation of this queue
	 * @param index Index
	 * @return Operation
	 */
	public Operation get(int index) {
		return (Operation)operations.elementAt(index);
	}
	/**
	 * Sets a operation at the specified index
	 * @param index Index to store the delivered Operation
	 * @param op The new Operation for the position at Index
	 */
	public void set(int index, Operation op) {
		rating -= ((Operation)operations.elementAt(index)).getRating();
		operations.setElementAt(op, index);
		rating += op.getRating();
	}
	
	/**
	 * Removes a operation at a specified index
	 * @param index Index of the to removed operation
	 */
	public void remove(int index) {
		rating -= ((Operation)operations.elementAt(index)).getRating();
		operations.removeElementAt(index);
	}
	
	/**
	 * Shuffles the operations in this queue
	 */
	public void shuffle() {
		int l = operations.size();
		for(int i=0; i<10; i++) {
			int i1 = (int)(Math.random()*(double)l);
			int i2 = (int)(Math.random()*(double)l);
			if(i1!=i2) {
				Object temp = operations.elementAt(i1);
				operations.setElementAt(operations.elementAt(i2),i1);
				operations.setElementAt(temp,i2);
			}
		}
	}
	/**
	 * Shuffles the operations so that DELETE_MIN lies inside the
	 * last 4 operations.
	 */
	public void shuffleDelmin() {
		shuffle();
		// letztes Delete-Min muss innerhalb der letzten 4 Operationen liegen
		int l = operations.size();
		if(l>4) {
			int delminIndex = -1;
			for(int i=l-1; i>=0; i--) {
				Operation op = (Operation)operations.elementAt(i);
				if(op.getID().equals("DELETE_MIN")) {
					delminIndex = i;
					break;
				}
			}
			if(delminIndex>=0&&delminIndex<l-4) {
				Object temp = operations.elementAt(l-4);
				operations.setElementAt(operations.elementAt(delminIndex),l-4);
				operations.setElementAt(temp,delminIndex);
			}
		}			
	}
	// Permutationen erzeugen
	private int permCounter;
	private OperationQueue[] permOQ;
	/**
	 * Gets all permutations of this OperationQueue
	 * @return permutations as OperationQueue's
	 */
	public OperationQueue[] getPermutations() {
		// Fakultätsberechnung
		int permSize = 1;
		for(int i=2; i<=length(); i++) permSize = permSize*i;
		permOQ = new OperationQueue[permSize];
		permCounter = 0;
		permut(0,length()-1);
		return permOQ;
	}
	//	Funktion zum Berechnen der Permutation von Ziffern
	private void permut(int d,int n) {
		Operation h = get(d);
		for(int i=d;i<=n;i++) {
			set(d, get(i));	// x[d]=x[i];
			set(i, h);		// x[i]=h;
			if (d < n) {
				permut(d+1,n);
			} else {
				//System.out.print("Permutation "+permCounter+": ");
				OperationQueue oq = new OperationQueue();
				for(int j=0; j<=i; ++j) {
					oq.add(get(j));		// System.out.print(x[j]+" ");
				}
				permOQ[permCounter] = oq;
				//System.out.println();
				permCounter++;
			}
			set(i,get(d));	// x[i]=x[d];
		}
		set(d,h);	// x[d]=h;
	}
	
	/**
	 * Converts this OperationQueue to an array
	 * @return array of Operation's
	 */
	public Operation[] toArray() {
		Operation[] ops = new Operation[length()];
		operations.toArray(ops);
		return ops;
	}
	/**
	 * Converts this OperationQueue to an vector
	 * @return vector of Operation's
	 */
	public Vector toVector() {
		return operations;
	}
	/**
	 * Gets the length
	 * @return length
	 */
	public int length() {
		return operations.size();
	}
	/**
	 * Returns the accumulated rating of all Operations 
	 * @return rating
	 */
	public int getRating() {
		return rating;
	}
	/**
	 * Sets the rating
	 * @param rating Rating
	 */
	public void setRating(int rating) {
		this.rating = rating;
	}
	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	// Sortieren nach Rating
	public int compareTo(Object o) {
		return rating - ((OperationQueue)o).getRating();	// aufsteigend
		//return ((Operation)o).getRating() - rating;	// absteigend
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	public Object clone() {
		OperationQueue oq = new OperationQueue();
		Enumeration en = operations.elements();
		while(en.hasMoreElements()) {
			Operation op = (Operation)en.nextElement();
			oq.add((Operation)op.clone());	
		}
		oq.setRating(rating);
		return oq;
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	// in String umwandeln (für Ausgabe)
	public String toString() {
		String s = "";
		Enumeration en = operations.elements();
		while(en.hasMoreElements()) {
			Operation op = (Operation)en.nextElement();
			s+=op+" | ";
		}
		s+= " Rating = "+rating;
		return s;
	}
	/**
	 * Executes all Operations in this queue on the data-structure,
	 * by calling the execute-Method of all its Operation's
	 * @param o data-structure-object
	 * @see mauda.operation.SimpleOperation#execute(Object)
	 */
	// Ganze OperationQueue  ausführen
	public void execute(Object o) {
		Enumeration en = operations.elements();
		while(en.hasMoreElements()) {
			Operation op = (Operation)en.nextElement();
			op.execute(o);
		}
	}
	/**
	 * Converts this queue in a vector-representation for saving-
	 * purposes.
	 * @return vector-representation of this queue
	 */
	// SAVE
	public Vector save() {
		Vector vector = new Vector();
		Enumeration en = toVector().elements();
		while(en.hasMoreElements()) {
			Operation op = (Operation)en.nextElement();
			//vector.add(op.save());	// ALT
			Vector v = new Vector();
			v.add(op.save());
			v.add(op.getSubOperationQueue().save());
			vector.add(v);
		}
		return vector;
	}
	/**
	 * Loads this OperationQueue with the values from a vector-
	 * representation readed from e.g. a xml-file.
	 * @param vector vector-representation
	 */
	// LOAD
	public void load(Vector vector) {
		operations = new Vector();
		rating = 0; //Integer.MIN_VALUE;
		Enumeration en = vector.elements();
		while(en.hasMoreElements()) {
			Vector v = (Vector)en.nextElement();
			String s = (String)v.elementAt(0);
			Operation op = new Operation();
			op.load(s);
			SubOperationQueue soq = new SubOperationQueue();
			soq.load((Vector)v.elementAt(1));
			op.setSubOperationQueue(soq);
			operations.add(op);
			/* ALT
			String s = (String)en.nextElement();
			Operation op = new Operation();
			op.load(s);
			operations.add(op);*/
		}
	}
}
