package ub.scp2.boImpl;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.faces.context.FacesContext;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;

import ub.scp2.bo.SubmissionBo;
import ub.scp2.dao.SubmissionDao;
import ub.scp2.entities.Submission;
import ub.scp2.entities.Task;
import ub.scp2.entities.Test;
import ub.scp2.entities.User;
import ub.scp2.externalExecutor.ExternalExecutor;

/**
 * SubmissionImpl.java -this class implements the SubmissionBo. This is the Business Object of the Submission entity
 * 
 *
 */
public class SubmissionBoImpl implements SubmissionBo, java.io.Serializable {
	private SubmissionDao submissiondao;

	private String path_prefix = "/tmp/";
	
	public void setSubmissiondao(SubmissionDao submissiondao) {
		this.submissiondao = submissiondao;
	}

	public void deleteSubmissionsByUser(User user) {
		submissiondao.deleteSubmissionsByUser(user);
	}

	/**
	 * Returns the submission of the given user for the given task, or a new submission if there isn't one yet.
	 */
	public Submission getThisSubmission(Task task, User user) {
		
		List<Submission> submissions = new ArrayList<Submission>(user.getSubmissions());
		Iterator<Submission> ite = submissions.iterator();
		Submission s;
		Submission result = new Submission();
		result.setUser(user);
		result.setTask(task);
		result.setMark(0);

                result.setFiles(new java.util.HashSet<ub.scp2.entities.FileSubmission>());
		
		while (ite.hasNext()){
			s = ite.next();
			if (s.getTask().getTaskId() == task.getTaskId()){
				result = s;
				break;
			}
		}
		return result;
	}
	
	// ******* CODE CHECKING METHODS *******
	
	/**
	 * Execute the test with the user code
	 */
	public List<String> runTest(Test test, User user){
		
		//Build the compilation command
		ArrayList<String> command = buildRunCommand(user, test);
		//Path to the sandbox
		WebApplicationContext ctx =  FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
		String absoluteSandboxPath = ctx.getServletContext().getRealPath("/WEB-INF/sandbox/");
		//Run the command
		ArrayList<String> result = run(command, absoluteSandboxPath);
		
		return result;
	}
	/**
	 * Create commands for execution
	 * @param user
	 * @param test
	 * @return
	 */
	public ArrayList<String> buildRunCommand(User user, Test test){
		ArrayList<String> command = new ArrayList<String>();
		
		//Path to the sandbox
		WebApplicationContext ctx =  FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
		String abslouteSandboxPath = ctx.getServletContext().getRealPath("/WEB-INF/sandbox/sandbox");
		//command.add(abslouteSandboxPath);
		
		//Name of the binary
		command.add(path_prefix + user.getUsername());
		
		//Arguments of the test
		if (test.getArguments() != ""){
			List<String> test_args = Arrays.asList(test.getArguments().trim().split(" "));
			command.addAll(test_args);
		}
		
		//DEBUG: print full command
		Iterator<String> ite = command.iterator();
		String finished_command = new String();
		while(ite.hasNext()){
			finished_command = finished_command + ite.next() +" ";
		}
		System.out.println("Command = " + finished_command);
		
		return command;
	}
	
	/**
	 * Returns the results of the compilation, 2 elements: first is stdout  and second stderr
	 */
	public List<String> compile(Submission submission, User user, Task task){
		
		//Save task's code to temporary file
		String temp_file = codeToTemporaryFile(submission, user, task);
		
		//Build the command
		ArrayList<String> command = buildCompileCommand(user, task, temp_file);
		
		//Run the command
		ArrayList<String> result = run(command, path_prefix);
		
		return result;
	}
	/**
	 * Delete temporary code file & temporary binary file
	 */
	public void cleanup(User user, Task task){
		
		String temp_file = path_prefix + user.getUsername() + task.getLanguaje().getFileExtension();
		
		//Delete temporary code file
		deleteTemporaryFile(temp_file);
		
		//Delete temporary binary file
		deleteTemporaryFile(path_prefix + user.getUsername());
	}
	
	/**
	 * Create commands for compile
	 * @param user
	 * @param task
	 * @param temp_file
	 * @return
	 */
	public ArrayList<String> buildCompileCommand(User user, Task task, String temp_file){
		ArrayList<String> command = new ArrayList<String>();

		if(".c".equals(task.getLanguaje().getFileExtension())){
			command.add("gcc");
			command.add("-o");
			command.add(path_prefix + user.getUsername());
			command.add(temp_file);
			if (task.getCompilerArguments() != ""){
				List<String> compiler_args = Arrays.asList(task.getCompilerArguments().trim().split(" "));
				Iterator<String> ite = compiler_args.iterator();
				while (ite.hasNext()){
					command.add(ite.next());
				}
			}
			
		}
		
		//DEBUG: print full command
		Iterator<String> ite = command.iterator();
		String finished_command = new String();
		while(ite.hasNext()){
			finished_command = finished_command + ite.next() +" ";
		}
		System.out.println("Command = " + finished_command);
		
		return command;
	}
	

	/**
	 * Saves the code of task to a temporary file in /tmp
	 * @param submission
	 * @param user
	 * @param task
	 * @return
	 */
	public String codeToTemporaryFile(Submission submission, User user, Task task){
		try{
			//Write code to a temporary file
			BufferedWriter out;
		    out = new BufferedWriter(new FileWriter(path_prefix + user.getUsername() + task.getLanguaje().getFileExtension()));
                    //TODO DEPRECATED/NOT USED. ALMOST SURE IT WON'T WORK. No preparado para multiples ficheros
                    System.out.println("//TODO DEPRECATED/NOT USED. ALMOST SURE IT WON'T WORK. No preparado para multiples ficheros");
		    //out.write(submission.getFiles().iterator().next().getCode());
		    out.close();
		    
		    return path_prefix + user.getUsername() + task.getLanguaje().getFileExtension();
		}catch(IOException e){
	    	e.printStackTrace();
	    	return null;
	    }
	}

	/**
	 * Deletes the given file
	 * @param filepath
	 * @return
	 */
	public boolean deleteTemporaryFile(String filepath){
		ArrayList<String> command = new ArrayList<String>();
		command.add("rm");
		command.add("-f");
		command.add(filepath);
		ArrayList<String> result = run(command, path_prefix);
		return result.get(0).equals("") && result.get(1).equals("");

	}
	
	/**
	 * Runs command, and returns an arraylist with 2 elements: first is stdout, second is stderr of command
	 * @param command
	 * @param cwd
	 * @return
	 */
	public ArrayList<String> run(ArrayList<String> command, String cwd){
		//Run command, first argument is the command, second argument is the current working directory (CWD)
		ExternalExecutor ee = new ExternalExecutor(command, cwd);
		ee.executeCommand();
		StringBuilder stdout = ee.getStandardOutputFromCommand();
		StringBuilder stderr = ee.getStandardErrorFromCommand();
		
		//DEBUG: print output
		System.out.println("STDOUT");
		System.out.println(stdout);
		System.out.println("STDERR");
		System.out.println(stderr);
		
		//Return value is true if there are no compilation errors in stderr
		ArrayList<String> result = new ArrayList<String>();
		result.add(stdout.toString());
		result.add(stderr.toString());
		return result;
	}
	
}
