import java.lang.Math;

/**
 * This class represents a shot transition.
 * A shot boundary or transition is defined by its pre frame and its post frame. 
 * These frames are respectively the last and first frame before and after the 
 * beginning of the transition. The transition could be a short one 
 * (CUT) with its post right after the pre frame or 
 * a gradual one with at least one frame within the transition effect.
 *
 * @author NIST-ITL-IAD-Retrieval Group
 * This software was produced by NIST, an agency of the
 * U.S. government, and by statute is not subject to copyright in the
 * United States. Recipients of this software assume all
 * responsibilities associated with its operation, modification and
 * maintenance.
 *
 */

public class Transition {

	private final static String[] types = {"CUT", "GRAD"};
	private final static int typeCount = 2;

	private static boolean[][] ConfusionMatrix = new boolean[typeCount][typeCount];
	private boolean matrixSetted = false;

	/**
     * The pre frame: the last frame of a previous shot without any content of the transition effect.
     *
     * @see #getPre()
     */
    public int pre;
	/**
     * The post frame: the first frame of the following shot without any content of the transition effect.
     *
     * @see #getPost()
     */
	public int post;
	/**
     * The type of transition: could take the value "cut" and anything else stands for a gradual transition.
     *
     */
	public String type;
	/**
     * Indicates wether the transition has matched with another one during a comparison.
     *
     */
	public int matched = 0;
	/**
     * Constructs Transition object with the given parameters.
     *
     * @param     pre The pre frame of the transition.
     * @param     post The post frame of the transition.
     * @param     type The type of trnasition.
     * @exception TransitionDefinitionException Thrown if the pre frame is not strictly less than the post frame.
     */
	public Transition(int pre, int post, String type)throws TransitionDefinitionException{
		if((pre >-1) && (post >-1) && (pre < post)){
			this.pre = pre;
			this.post = post;
			if(type.compareToIgnoreCase("CUT") == 0)
				this.type = "CUT";
			else
				this.type = type;
			if(!matrixSetted){
				for(int i = 0 ; i < typeCount; i++)
					setConfusion(types[i], types[i]);
				matrixSetted = true;
			}
		}
		else throw new TransitionDefinitionException("Transition definition error: pre and post not well ordered");
	}
	/**
     * Sets the confusion between one type and another. Note that anything else than "CUT" whatever the string case
     * is considered as gradual...
     *
     * @param     type1 A first type of transition.
     * @param     type2 A second type of transition.
     * @exception TransitionDefinitionException Thrown if one type checking returns an error.
     */
	public static void setConfusion(String type1, String type2)throws TransitionDefinitionException{
		
		int noType1 = checkType(type1);
		int noType2 = checkType(type2);
		if(noType1 > -1 && noType2 > -1){
			ConfusionMatrix[noType1][noType2] = true;
			ConfusionMatrix[noType2][noType1] = true;
		}
		else throw new TransitionDefinitionException("Transition Confusion not setted : Type definition error");
	}
	/**
     * Returns true if the given type has to be confused during a comparison.
     *
     * @param     type1 A first type of transition.
     * @param     type2 A second type of transition.
     */
	public static boolean getConfusion(String type1, String type2){
		return ConfusionMatrix[checkType(type1)][checkType(type2)];
	}
	/**
     * Gets the pre frame.
     *
     */
	public int getPre(){
		return this.pre;
	}
	/**
     * Gets the post frame.
     *
     */
	public int getPost(){
		return this.post;
	}
	/**
     * Returns 0 if the type is a "CUT" ignoring the case. Otherwise returns 1.
     *
     */
	private static int checkType(String type){

		boolean existingType = false;
		int i = 0;
		if(type.compareToIgnoreCase("CUT") == 0) return 0;
		else return 1;		
	}
	/**
     * Returns true if the given transition has the same type as the current instance 
     * regarding the setted confusions between types.
     *
     */
	public boolean sameType(Transition t){
		return (ConfusionMatrix[checkType(t.type)][checkType(this.type)]);
	}
	/**
     * Sets the pre frame value with the given shifting amount.
     *
     */
	public void adjustPre(int shiftingAmount){
		this.pre += shiftingAmount;
	}
	/**
     * Sets the post frame value with the given shifting amount.
     *
     */
	public void adjustPost(int shiftingAmount){
		this.post += shiftingAmount;
	}
	/**
     * Returns the length of the transition (post - pre).
     *
     */
	public int length(){
		return (this.post - this.pre );		
	}
	/**
     * Returns a transition representing the intersection with the given transition.
     *
     */
	public Transition intersection(Transition t){
	
		try{
			return (new Transition(Math.max(this.pre, t.pre), 
					Math.min(this.post, t.post), this.type));
		}
		catch(Exception e){
			return null;			
		}
	}
	/**
     * Returns true if there is an intersection with the given transition.
     *
     */
	public boolean intersected(Transition t){
			return (Math.max(this.pre, t.pre) < Math.min(this.post, t.post));
	}	
	/**
     * Returns an Xml style string of transition.
     *
     */
	public String toString(){
		return "<trans type=\""+this.type+"\" pre=\""+this.pre+"\" post=\""+this.post+"\"/>"+System.getProperty("line.separator");	
	}
	/**
     * Returns an Xml style string of transition with the Xml element name equal to "sysTrans".
     *
     */
	public String sysToString(){
		return "<sysTrans type=\""+this.type+"\" pre=\""+this.pre+"\" post=\""+this.post+"\"/>"+System.getProperty("line.separator");	
	}
	/**
     * Returns an Xml style string of transition with the Xml element name euqal to "refTrans".
     *
     */
	public String refToString(){
		return "<refTrans type=\""+this.type+"\" pre=\""+this.pre+"\" post=\""+this.post+"\"/>"+System.getProperty("line.separator");	
	}
	
}
