package extractors;

import io.*;

import java.io.*;
import java.util.*;

import extractors.hog.*;

/**
 * This class extracts the feature vectors of a database of models using a {@link FVExtractor} and prints them
 * in a feature vector file.
 * 
 */
public class FVFileWriter {

	// root directory of the database
	private File rootDir;

	// file to print the results to
	private PrintWriter fvFile;

	// extraction method
	private FVExtractor fvExtractor;

	// classifiedModels=parameter
	private int classifiedModels, modelID, classID;

	/**
	 * Constructs new {@link FVFileWriter} with given root directory and extraction method.
	 * 
	 * @param rootDir
	 * @param fvExtractor
	 * @throws FileNotFoundException
	 */
	public FVFileWriter(File rootDir, FVExtractor fvExtractor) throws FileNotFoundException {
		if (!rootDir.exists() || !rootDir.isDirectory()) {
			throw new FileNotFoundException("Root directory not found.");
		}
		this.rootDir = rootDir;
		this.fvExtractor = fvExtractor;
	}

	/**
	 * Writes the fv file. Classifies only numOfClassifiedModels models. Following models are left
	 * unclassified.
	 * 
	 * @param fvFileName
	 * @param numOfClassifiedModels
	 * @throws FileNotFoundException
	 */
	public void writeFVFile(String fvFileName, int numOfClassifiedModels) throws FileNotFoundException {
		fvFile = new PrintWriter(fvFileName);
		modelID = 1;
		classID = 0;
		classifiedModels = numOfClassifiedModels;

		writeHeader();
		processDir(rootDir);
		fvFile.flush();
		fvFile.close();
	}

	// processes a directory recursively
	private void processDir(File dir) {
		File[] offList = dir.listFiles(new OFFFilenameFilter());
		File[] dirList = dir.listFiles(new DirFilter());
		Arrays.sort(dirList);

		if (offList.length != 0) {
			classID++;
		}

		// iterate over .off-files and append the feature vector to the fv file
		for (File file : offList) {
			try {
				appendFV(fvExtractor.getFV(file), file.getPath());
			} catch (CorruptOFFFileException ex) {
				System.out.println(file.getPath() + ": Illegal Face. Writing dummy FV.");
				double[] fv = new double[fvExtractor.getDimension()];
				appendFV(fv, file.getPath());
			}
		}

		for (File file : dirList) {
			processDir(file);
		}
	}

	// appends a feature vector to the end of the fv file
	private void appendFV(double[] fv, String path) {
		int cid = modelID > classifiedModels ? -1 : classID;
		fvFile.print(path.substring(path.indexOf(File.separator)) + " " + modelID + " " + cid + " ");
		for (double d : fv) {
			fvFile.print(d + " ");
		}
		fvFile.print("\n");
		modelID++;
	}

	// writes the dimension of the FVExtractor to the fv file
	private void writeHeader() {
		fvFile.println(fvExtractor.getDimension());
	}

	// parse int parameter
	private static int getIntParameter(List<String> argList, String parameter) {
		int i, arg = -1;
		if ((i = argList.indexOf(parameter)) != -1) {
			String o = argList.get(i + 1);
			arg = Integer.parseInt(o);
			argList.remove(parameter);
			argList.remove(o);
		}
		return arg;
	}

	// parse String parameter
	private static String getStringParameter(List<String> argList, String parameter) {
		int i;
		String arg = null;
		if ((i = argList.indexOf(parameter)) != -1) {
			arg = argList.get(i + 1);
			argList.remove(parameter);
			argList.remove(arg);
		}
		return arg;
	}

	/**
	 * Return a time length in a pretty format.
	 * 
	 * @param etl
	 * @return
	 */
	public static String prettyprint(double etl) {
		int hours = (int) etl / 3600000;
		int min = (int) (etl % 3600000) / 60000;
		int sec = (int) (etl % 60000) / 1000;

		return hours + "h " + min + "m " + sec + "s";
	}


	// main method for HOG Extractor

	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		List<String> argList = new LinkedList<String>();
		for (String string : args) {
			argList.add(string);
		}

		int cnum = getIntParameter(argList, "-cnum");
		cnum = cnum > 0 ? cnum : Integer.MAX_VALUE;

		int res = getIntParameter(argList, "-res");
		if (res < 0) {
			throw new IllegalArgumentException("Specify -res for resolution of .raw-files.");
		}

		String propFile = getStringParameter(argList, "-paramFile");

		try {
			FVExtractor fve = HOGExtractor.hogExtractor(propFile, new DistanceFieldGenerator(res, res, res));
			String filename = argList.get(0);
			System.out.println("Extracting " + fve + " from " + filename);
			FVFileWriter fvw = new FVFileWriter(new File(filename), fve);
			fvw.writeFVFile(fve.toString() + ".fv", cnum);
			System.out.println("Saved to: " + fve.toString() + ".fv");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Done in: " + prettyprint(System.currentTimeMillis() - startTime));
	}
}
