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.Date;
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.externalExecutor.ExternalExecutor;

import ub.scp2.bo.TaskBo;
import ub.scp2.dao.TaskDao;
import ub.scp2.dao.TestDao;
import ub.scp2.entities.Subject;
import ub.scp2.entities.Submission;
import ub.scp2.entities.Task;
import ub.scp2.entities.Test;
import ub.scp2.entities.User;

/**
 * TaskBoImpl.java -this class implements the TaskBo. This is the Business Object of the Task entity
 * 
 *
 */
public class TaskBoImpl implements TaskBo, java.io.Serializable{

	private TaskDao taskdao;
	
	private String path_prefix = "/tmp/";

	// ******* PERSISTENCE METHODS *******
	public List<Task> getActiveTasksByUser(User user){
		return taskdao.getActiveTasksByUser(user);
	}
	
	public List<Task> getExpiredTasksByUser(User user){
		return taskdao.getExpiredTasksByUser(user);
	}
	
	public List<Test> getTestsByTask(Task task) {
		/*Task t = taskdao.getTaskById(task.getTaskId());
		ArrayList<Test> tests = new ArrayList<Test>(t.getTests());
		*/
		return taskdao.getTestByTask(task.getTaskId());
	}
	
	public List<Task> getTasks() {
		return taskdao.getTasks();
	}

	public void saveTask(Task task){
		taskdao.saveTask(task);
	}
	
	public void deleteTask(Task task){
		taskdao.deleteTask(task);	
	}
	
	public void updateTask(Task task){
		taskdao.updateTask(task);	
	}
	
	public boolean exists(Long taskid, User user) {
		return taskdao.exists(taskid, user);
	}
	
	public Task getTaskById(Long taskid) {
		return taskdao.getTaskById(taskid);
	}
	
	public void setTaskdao(TaskDao taskdao) {
		this.taskdao = taskdao;
		
	}
	

	// ******* CODE CHECKING METHODS *******
	/**
	 * Execute the test with the user code
	 */
	public List<String> runTest(Task task, Test test){
		
		//Save task's code to temporary file
		String temp_file = codeToTemporaryFile(task);
		
		//Build the compilation command
		ArrayList<String> command = buildCompileCommand(task, temp_file);
		
		//Compile
		run(command, path_prefix);
		
		//Build the execution command
		command = buildRunCommand(task, test);
	
		//Path to the sandbox
		WebApplicationContext ctx =  FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
		String absoluteSandboxPath = ctx.getServletContext().getRealPath("/WEB-INF/sandbox/");
		
		System.out.println("command=== "+command);
		//Run the command
		ArrayList<String> result = run(command, absoluteSandboxPath);
		
		return result;
	}
	/**
	 * Create commands for execution
	 * @param user
	 * @param test
	 * @return
	 */
	public ArrayList<String> buildRunCommand(Task task, 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 + task.getUser().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(Task task){
		
		//Save task's code to temporary file
		String temp_file = codeToTemporaryFile(task);
		
		//Build the command
		ArrayList<String> command = buildCompileCommand(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(Task task){
		
		String temp_file = path_prefix + task.getUser().getUsername() + task.getLanguaje().getFileExtension();
		
		//Delete temporary code file
		deleteTemporaryFile(temp_file);
		
		//Delete temporary binary file
		deleteTemporaryFile(path_prefix + task.getUser().getUsername());
	}
	
	/**
	 * Create commands for compile
	 * @param task
	 * @param temp_file
	 * @return
	 */
	public ArrayList<String> buildCompileCommand(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 + task.getUser().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 task
	 * @return
	 */
	public String codeToTemporaryFile(Task task){
		try{
			//Write code to a temporary file
			BufferedWriter out;
		    out = new BufferedWriter(new FileWriter(path_prefix + task.getUser().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(task.getFiles().iterator().next().getCode());
		    out.close();
		    
		    return path_prefix + task.getUser().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;
	}

	public List<Task> getActiveTasksForStudent(User user) {
		//Get all user subjects
		List<Subject> subjects = new ArrayList<Subject>(user.getSubjects());
		Iterator<Subject> ite = subjects.iterator();
		Subject s;
		List<Task> tasks = new ArrayList<Task>();
		while (ite.hasNext()){
			s = ite.next();
			tasks.addAll(s.getTasks());
		}
		Iterator<Task> ite2 = tasks.iterator();
		Task t;
		List<Task> tasksToRemove = new ArrayList<Task>();
		while (ite2.hasNext()){
			t = ite2.next();
			if (t.getEndDate().before(new Date()) || t.getStartDate().after(new Date())){
				tasksToRemove.add(t);
			}
		}
		tasks.removeAll(tasksToRemove);		
		return tasks;
	}
	
	public List<Task> getExpiredTasksForStudent(User user) {
		List<Subject> subjects = new ArrayList<Subject>(user.getSubjects());
		Iterator<Subject> ite = subjects.iterator();
		Subject s;
		List<Task> tasks = new ArrayList<Task>();
		while (ite.hasNext()){
			s = ite.next();
			tasks.addAll(s.getTasks());
		}
		Iterator<Task> ite2 = tasks.iterator();
		Task t;
		List<Task> tasksToRemove = new ArrayList<Task>();
		while (ite2.hasNext()){
			t = ite2.next();
			if (t.getEndDate().after(new Date())){
				tasksToRemove.add(t);
			}
		}
		tasks.removeAll(tasksToRemove);
		
		return tasks;
	}

        
        public List<String[]> getPresetArguments(String path){
            return taskdao.getPresetArguments(path);
        }
}
