package extraction;

import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

public class SingleTrackExtractor {

	private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
	
	private static final Logger logger = Logger.getLogger(SingleTrackExtractor.class);

	private final File destinationDirectory;

	private final File temporaryLogFile = new File("/tmp/SingleTrackExtractor.log");

	/**
	 * Constructor.
	 * 
	 * @param destinationDirectory File pointing at the directory where the tracks must be stored.
	 * 
	 * @throws IOException thrown if destination directory does not exist and cannot be created.
	 */
	public SingleTrackExtractor(File destinationDirectory) throws IOException {

		if (destinationDirectory == null) {
			throw new NullPointerException("Null destination directory path.");
		}

		if (!destinationDirectory.isDirectory()) {
			throw new IllegalArgumentException("Invalid destination directory: "+destinationDirectory.getAbsolutePath());
		}

		if (!destinationDirectory.exists()) {

			logger.warn("constructor: Inexistent destination directory: '"+destinationDirectory.getAbsolutePath()+"'. Going to create it.");

			if (destinationDirectory.mkdirs()) {

				logger.info("SingleTrackExtractor: Destination directory successfully created: "+destinationDirectory.getAbsolutePath());
			}
			else {
				throw new IOException("Could not create destination directory at '"+destinationDirectory.getAbsolutePath()+"'.");
			}
		}		

		this.destinationDirectory = destinationDirectory;

		logger.info("SingleTrackExtractor: Destination directory set to '"+this.destinationDirectory.getAbsolutePath()+"'");
		logger.info("SingleTrackExtractor: Temporary log file for this class created at '"+this.temporaryLogFile.getAbsolutePath()+"'");
	}	

	public File extract(File sourceAudioFile, LocalTime startTime, LocalTime duration, String trackName) {

		if (sourceAudioFile == null) {
			logger.error("extract: Null source audio file.");
			return null;
		}
		
		if (!sourceAudioFile.exists() || !sourceAudioFile.isFile()) {
			logger.error("extract: Invalid source audio file.");
			return null;
		}
		
		if (startTime == null) {
			logger.error("extract: Null start time.");
			return null;
		}
		
		if (duration == null) {
			logger.error("extract: Null duration.");
			return null;
		}
		
		if (trackName == null) {
			logger.error("extract: Null track name.");
			return null;
		}
		
		if (trackName.trim().isEmpty()) {
			logger.error("extract: Empty track name.");
			return null;
		}
		
		String destinationTrackPath = new File(this.destinationDirectory, trackName).getAbsolutePath();							

		String command = this.createCommand(sourceAudioFile, startTime, duration, destinationTrackPath);

		ProcessBuilder builder = new ProcessBuilder(Arrays.asList(new String[]{
				"/bin/bash",
				"-c",
				command
		}));

		builder.redirectOutput(Redirect.appendTo(this.temporaryLogFile));
		builder.redirectError(this.temporaryLogFile);

		String invocationDescription = new StringBuilder("Source: ")
				.append(sourceAudioFile.getAbsolutePath())
				.append(", start: ")
				.append(startTime)
				.append(", duration: ")
				.append(duration)
				.toString();
		
		try {
			Process process = builder.start();					

			if (process.waitFor(30, TimeUnit.SECONDS)) {
				logger.info("extract: "+invocationDescription+". Successfully extracted: "+destinationTrackPath);
			}
			else {
				logger.error("extract: "+invocationDescription+". Could not execute: "+command);
			}
		}
		catch (Exception e) {
			logger.error("extract: Could not extract: "+invocationDescription);
		}
		
		File extractedTrackFile = new File(destinationTrackPath);
		
		if (!extractedTrackFile.exists()) {
			
			logger.error("generateFilePath: Process completed normally but track was not extracted: "+destinationTrackPath);
			
			return null;
		}
		
		return extractedTrackFile;
	}

	private final String createCommand(File audioFile, LocalTime start, LocalTime duration, String destinationFile) {

		return new StringBuilder("ffmpeg -i ")
				.append(audioFile.getAbsolutePath())
				.append(" -t ").append(this.formatter.format(duration))
				.append(" -ss ").append(this.formatter.format(start))
				.append(" -acodec ")
				.append(" copy ").append(destinationFile)
				.toString();
	}
}
