import java.util.*;
import java.io.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.*;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import org.xml.sax.*;

/**
 * This class is the main in processing the comparison of a system's shot boundaries segmentations and some references.
 * It permit to launch the parsing of Xml files without worrying about parsing errors
 * and also to process the comparison of system's parsed segmentations with reference segmentations.
 *
 * @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 ComparisonManager{

    /**
     * The reference direcotry.
     *
     */
	public static String refDirName = null;
	/**
     * The comparator instance for comparison between short graduals and CUT transitions.
     *     
     */
	private ApproximateComparator rone = ApproximateComparator.getInstance();
    /**
     * The comparator instance for overlapping, used with its default parameter values for RMin and RMax.
     *     
     */
    private OverlapComparator overlap = OverlapComparator.getInstance();
    /**
     * The Hashtable containing the file names of the reference.
     * The key is the video name and value is the file name.
     *     
     */
	private Hashtable referenceFilenames;
	/**
     * The Hashtable containing the reference segmentations.
     * The key is the video name and value is the segmentation.
     * 
     */
	private Hashtable referenceSegmentations = new Hashtable();
	/**
     * The Hashtable containing the system results.
     * The key is the system ID and value is a systemResult instance.
     * 
     */
	private Hashtable systemResults;
	
	public ComparisonManager(){}
    /**
     * Gets the full path to the reference directory.
     *
     */
	static protected String getFullRefDirName() {
		return System.getProperty("user.dir") +System.getProperty("file.separator") + refDirName;
	}

    /**
     * Overrule refDir, only if directory exists
     *
     */
	static protected void setRefDirName(String theRefDirName) {
		refDirName = theRefDirName;
		File theRefDir = new File(refDirName);
    	if (!theRefDir.exists()) 
			theRefDir = new File(getFullRefDirName());
		if (!theRefDir.exists()) {
			System.err.println("Error: Cannot open directory " + refDirName);
			usage();
			System.exit(1);
		}
	}
	/**
     * Launch the parsing of the file containing the mapping between a video file name 
     * and an Xml segmentation reference file.
	 *
     * @param File referenceFileNamesFile name of the file containing this informations
     */   
	public void parseReferenceFileNames(File referenceFileNamesFile){

		//Test the existence of the file
		if (!referenceFileNamesFile.exists()){
           	System.err.println("The \"references files names\" file \""+referenceFileNamesFile.getAbsolutePath()+"\""+
           	System.getProperty("line.separator")+" does not EXISTS");
           	System.exit(1);	
		}
		System.out.println(System.getProperty("line.separator")+"Parsing \"references files names\" Xml file..."); 
		// Use the validating parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //factory.setValidating(true);
        try {
			Writer out = new OutputStreamWriter(System.out, "UTF8");
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
			shotResultContentHandler resultHandler = new shotResultContentHandler(out);

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            if(referenceFileNamesFile == null || resultHandler == null){
            	System.out.println("NULL");
            	System.exit(0);
            }
            saxParser.parse(referenceFileNamesFile, resultHandler);
            this.referenceFilenames = resultHandler.getReferenceFileNames();
            //System.out.println(this.referenceFilenames);
        } catch (SAXParseException spe) {
           // Error generated by the parser
           System.out.println("\n** Parsing error"
              + ", line " + spe.getLineNumber()
              + ", uri " + spe.getSystemId());
           System.out.println("   " + spe.getMessage() );

           // Use the contained exception, if any
           Exception  x = spe;
           if (spe.getException() != null)
               x = spe.getException();
           x.printStackTrace();

        } catch (SAXException sxe) {
           // Error generated by this application
           // (or a parser-initialization error)
           Exception  x = sxe;
           if (sxe.getException() != null)
               x = sxe.getException();
           x.printStackTrace();

        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();

        } catch (IOException ioe) {
           // I/O error
           ioe.printStackTrace();
    	}
	}

	/**
     * Launch the parsing of the file containing the Xml reference segmentations.
	 *
     */	
	public void parseReference(){
		
		// Use the validating parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setValidating(true);
        try {
			Writer out = new OutputStreamWriter(System.out, "UTF8");
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
			shotResultContentHandler resultHandler = new shotResultContentHandler(out);

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            if(resultHandler == null){
            	System.out.println("NULL");
            	System.exit(0);
            }
            
            //For each systemResult in systemResults Hashtable retrieve all videoNames
            //and then find out the reference.xml matching file to parse
            
            Hashtable xmlRefFiles = new Hashtable();//key=.xml, value==video.mpg
            Enumeration e = systemResults.elements();            
            while(e.hasMoreElements()){
            
            	systemResult s = (systemResult) e.nextElement();
            	Enumeration n = s.getSegmentationsVideoNames().elements();
            	while(n.hasMoreElements()){
            		String someVideo = (String) n.nextElement();
            		if (referenceFilenames.containsKey(someVideo))
            			xmlRefFiles.put(referenceFilenames.get(someVideo), someVideo);
            	}
            }
            
            //Now we have to parse and then get
            //by getRefSegmentation() from ContentHandler all reference Segmentations

            e = xmlRefFiles.keys();
			System.out.println(System.getProperty("line.separator")+"Parsing reference segmentation Xml files..."); 
            while(e.hasMoreElements()){
            	
	            String xmlFile = (String) e.nextElement();
	            File toParse = new File(System.getProperty("user.dir")+
										System.getProperty("file.separator")+
										refDirName+
										System.getProperty("file.separator")+
										xmlFile);
				//Test the existence of the file
				if (!toParse.exists()){
           			System.err.println("The specified file \""+toParse.getAbsolutePath()+"\""+
           			System.getProperty("line.separator")+" does not EXISTS");
           			System.exit(1);
				}
	            saxParser.parse(toParse, resultHandler);
	            this.referenceSegmentations.put((String) xmlRefFiles.get(xmlFile), resultHandler.getRefSegmentation());	            
            }
            //System.out.println(this.referenceSegmentations);

        } catch (SAXParseException spe) {
           // Error generated by the parser
           System.out.println("\n** Parsing error"
              + ", line " + spe.getLineNumber()
              + ", uri " + spe.getSystemId());
           System.out.println("   " + spe.getMessage() );

           // Use the contained exception, if any
           Exception  x = spe;
           if (spe.getException() != null)
               x = spe.getException();
           x.printStackTrace();

        } catch (SAXException sxe) {
           // Error generated by this application
           // (or a parser-initialization error)
           Exception  x = sxe;
           if (sxe.getException() != null)
               x = sxe.getException();
           x.printStackTrace();

        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();

        } catch (IOException ioe) {
           // I/O error
           ioe.printStackTrace();
    	}
	}
    /**
     * Launch the parsing of the file containing the results per system results.
	 *
     * @param systemResultsFile name of the file containing this information.
     */
	public void parseResults(File systemResultsFile){
		
		//Test the existence of the file
		if (!systemResultsFile.exists()){
           	System.err.println("The specified file \""+systemResultsFile.getAbsolutePath()+"\""+
           	System.getProperty("line.separator")+" does not EXISTS or should be in a sub-directory called \"System\"");
           	System.exit(1);			
		}
		System.out.println(System.getProperty("line.separator")+"Parsing system segmentations Xml file..."); 
		
        // Use the validating parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setValidating(true);
        try {
			Writer out = new OutputStreamWriter(System.out, "UTF8");
            // Set up output stream
            out = new OutputStreamWriter(System.out, "UTF8");
			shotResultContentHandler resultHandler = new shotResultContentHandler(out);

            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            if(systemResultsFile == null || resultHandler == null){
            	System.out.println("NULL");
            	System.exit(0);
            }
            saxParser.parse(systemResultsFile, resultHandler);
            this.systemResults = resultHandler.getSystemResults();
            //System.out.println(this.systemResults);

        } catch (SAXParseException spe) {
           // Error generated by the parser
           System.out.println("\n** Parsing error"
              + ", line " + spe.getLineNumber()
              + ", uri " + spe.getSystemId());
           System.out.println("   " + spe.getMessage() );

           // Use the contained exception, if any
           Exception  x = spe;
           if (spe.getException() != null)
               x = spe.getException();
           x.printStackTrace();

        } catch (SAXException sxe) {
           // Error generated by this application
           // (or a parser-initialization error)
           Exception  x = sxe;
           if (sxe.getException() != null)
               x = sxe.getException();
           x.printStackTrace();

        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            pce.printStackTrace();

        } catch (IOException ioe) {
           // I/O error
           ioe.printStackTrace();
    	}
	}
	/**
     * Process the comparison between the reference segmentations and each system segmentation.
     *     
     */
	public void processComparison() throws Exception{

		//For each systemResult object
		Enumeration e = systemResults.elements();            
		while(e.hasMoreElements()){
        
        	systemResult s = (systemResult) e.nextElement();
			//For each Segmentation in the current systemResult
			Enumeration n = s.getSegmentations();
        	while(n.hasMoreElements()){
        		SysSegmentation aSystemSeg = (SysSegmentation) n.nextElement();
        		RefSegmentation aRefSeg = (RefSegmentation) referenceSegmentations.get(aSystemSeg.getVideoName());
        		//We compare the two segmentations
        		//Creation of a comparisonResult
				aRefSeg.processComparison(s.getSysId(), aSystemSeg);
        	}
    	}
	}
	/**
     * Adjust the system transition's pre and post values according to the given shifting amount.
     *     
     */
	public void adjustSystemResult(int shiftingAmount) throws Exception{

		//For each systemResult object
		Enumeration e = systemResults.elements();
		while(e.hasMoreElements()){
        
        	systemResult s = (systemResult) e.nextElement();
			//For each Segmentation in the current systemResult
			Enumeration n = s.getSegmentations();
        	while(n.hasMoreElements()){
        		Segmentation aSystemSeg = (Segmentation) n.nextElement();
				Iterator transitionIterator = aSystemSeg.getElements();
				while(transitionIterator.hasNext()){
					Transition t = (Transition) transitionIterator.next();
					t.adjustPre(shiftingAmount);
					t.adjustPost(shiftingAmount);
				}
        	}
    	}
	}

	/**
	 * Print usage message
	 */
	public static void usage() {
            System.err.println("Usage: java ComparisonManager [systemSeg_file.xml] [-notype] [-rone] [-rmax] [-rmin] [-refDir dir]");
            System.err.println("Examples:");
            System.err.println("  java ComparisonManager Sys_MY_SYSTEM_SB.xml -notype -rmax0.6666 -rmin0.3333");
            System.err.println("  # This will set rMin parameter to 0.6666, and pSys to 0.3333");
            System.err.println("  # rmax = Minimum overlapping pourcentage of the shorter transition");
            System.err.println("  # rmin = Minimum overlapping pourcentage of the longer transition");
            System.err.println("  # Default values are 0.4999 for rmax,0.3333 for rmin");
	}
	/**
     * Main entry point to the program.
     *     
     */
    public static void main(String argv[]){

		if ((argv.length < 1) || (argv.length > 5)) {
            usage();
            System.exit(1);
        }
        else{
        	for(int i=1; i < argv.length; i++){
        		
        		if(argv[i].equals("-notype"))
        			try{
        				Transition.setConfusion("CUT", "GRAD");
        			} catch(Exception e){ }
        		if(argv[i].startsWith("-rone"))
        			ApproximateComparator.setRone(Double.parseDouble(argv[i].substring(1+argv[i].lastIndexOf("e"))));
        		if(argv[i].startsWith("-rmax"))
        			OverlapComparator.setRMax(Double.parseDouble(argv[i].substring(1+argv[i].lastIndexOf("x"))));
        		if(argv[i].startsWith("-rmin"))
        			OverlapComparator.setRMin(Double.parseDouble(argv[i].substring(1+argv[i].lastIndexOf("n"))));
        		if (argv[i].equals("-refDir")) {
			  		if (++i>argv.length) {
			    		usage();
			    		System.exit(1);
			  		}
			  		setRefDirName( argv[i] );
				}
        	}
        	if (refDirName == null) setRefDirName("Reference");
			ComparisonManager c = new ComparisonManager();
			c.parseResults(new File(System.getProperty("user.dir")+
									System.getProperty("file.separator")+
									"System"+
									System.getProperty("file.separator")+
									argv[0]));

			c.parseReferenceFileNames(new File(System.getProperty("user.dir")+
											   System.getProperty("file.separator")+
											   refDirName+
											   System.getProperty("file.separator")+
											   "shotBoundaryReferenceFiles.xml"));
			c.parseReference();			
			try{
				c.processComparison();
			}
			catch(Exception e){ e.printStackTrace(); }
		}
	}
}