package ub.scp2.managedBeans;

import infoObjects.InfoError;
import infoObjects.InfoTarea;
import infoObjects.InfoTest;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;

import org.apache.commons.io.IOUtils;
import org.primefaces.context.RequestContext;
import org.primefaces.model.UploadedFile;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;

import ub.scp2.bo.TaskBo;
import ub.scp2.bo.TestBo;
import ub.scp2.entities.Subject;
import ub.scp2.entities.Task;
import ub.scp2.entities.Test;
import ub.scp2.entities.User;
import utils.ConstantsErr;
import utils.RMIServices;

/**
 * TestBean.java - a Bean to control the Add Test view
 *
 *
 */
@ManagedBean(name = "testBean")
@ViewScoped
public class TestBean implements Serializable, ConstantsErr {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private TestBo testbo;
    private TaskBo taskbo;

    private Task task;

    private Test selectedTest;
    private Test newTest;
    private String newTestOutput;
    private String newTestError;
    private String auxArguments;
    private Date today;
    private boolean noStartDate;

    private ArrayList<String> allArguments;

    private boolean submitDisabled;

    private int idArgument;
    private UploadedFile docFile;
    private UploadedFile docFile2;
    private UploadedFile docFile3;

    private List<Test> testlist;

    @ManagedProperty(value = "#{loginBean}")
    private LoginBean loginbean;

    /**
     * Initialize the parameters required for the class
     */
    @PostConstruct
    public void init() {
        task = loginbean.getTask();
        WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        testbo = ctx.getBean(TestBo.class);
        taskbo = ctx.getBean(TaskBo.class);
        newTest = new Test();
        today = new Date();
        noStartDate = true;
        submitDisabled = true;
        newTestError = "";
        newTestOutput = "";
        auxArguments = "";
        idArgument = 99;
        getTest();

    }

    public void check() {
        if (task == null) {
            try {
                FacesContext.getCurrentInstance().getExternalContext().redirect("teacher.xhtml");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            calcularData(); //Comentar para quitar la restriccion de no crear test si la tarea ha empezado
        }
    }

    /**
     * Delete the selected test
     */
    public void deleteTest() throws MalformedURLException, RemoteException, NotBoundException {

        if (selectedTest != null) {
            try {
                if (!RMIServices.isConnected()) {
                    RMIServices.connectRMI();
                }
                InfoTest infoTest = new InfoTest(selectedTest.getTestId(), selectedTest.getTask().getTaskId());
                InfoError infoError = RMIServices.securityDeleteTest(infoTest);
                testbo.deleteTest(selectedTest);
                if (infoError.getError() == ERROR_OK) {
                    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Test deleted.", "Test deleted.");
                    FacesContext.getCurrentInstance().addMessage(null, message);
                    this.getTest();
                } else {
                    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error deleting test in SSCP:" + infoError.getError(), "Error deleting test.");
                    FacesContext.getCurrentInstance().addMessage(null, message);
                }
            } catch (Exception e) {
                FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error deleting test.", "Error deleting test.");
                FacesContext.getCurrentInstance().addMessage(null, message);
            }
        } else {
            System.out.println("SelectedTest is null");
        }
    }

    public TestBean() {
    }

    /**
     * Run the program with the tests
     *
     * @throws NotBoundException
     * @throws RemoteException
     * @throws MalformedURLException
     */
    public void runNewTest() throws MalformedURLException, RemoteException, NotBoundException {
        //List<String> result = taskbo.runTest(task, newTest);

        if (!RMIServices.isConnected()) {
            RMIServices.connectRMI();
        }
        System.out.println("TIPOTEST: " + idArgument);

        //new ArrayList<String>(Arrays.asList(newTest.getArguments().trim().split(" ")));
        InfoTest infoTest;
        if (idArgument == 0) {
            infoTest = new InfoTest(task.getTaskId(), allArguments, idArgument);
        } else {
            //allArguments.add(newTest.getArguments());
            infoTest = new InfoTest(task.getTaskId(), allArguments, idArgument);
            Iterator<String> ite = infoTest.getArgumentos().iterator();
            String finished = new String();
            while (ite.hasNext()) {
                finished = finished + ite.next() + " ";
            }
            System.out.println("Contenido = " + finished);
        }

        System.out.println("IDTEST: " + newTest.getTestId());;
        System.out.println("TIPOTEST: " + idArgument);
        System.out.println("ARGUMENTOS: " + newTest.getArguments());
        System.out.println("allArguments: " + allArguments);

        InfoError infoError = RMIServices.securityRunTest(infoTest);

        newTestOutput = infoError.getOut();
        newTestError = infoError.getErr();
        taskbo.cleanup(task);

        submitDisabled = !newTestError.isEmpty();

        if (submitDisabled) {
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error running test: " + infoError.getErr(), "");
            FacesContext.getCurrentInstance().addMessage(null, msg);
        } else {
            newTest.setOutput(infoError.getOut());
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Test executed correctly", "");
            FacesContext.getCurrentInstance().addMessage(null, msg);
        }

    }

    /**
     * Check if the task has begun
     */
    public void calcularData() {
        //setNoStartDate(task.getStartDate().after(this.today));
        this.setNoStartDate(true);
    }

    /**
     * Save the new test set
     *
     * @throws NotBoundException
     * @throws RemoteException
     * @throws MalformedURLException
     */
    public void saveNewTest() throws MalformedURLException, RemoteException, NotBoundException {
        newTest.setTask(task);

        System.out.println("TIPOTEST " + idArgument);
        System.out.println("getArguments " + newTest.getArguments());
        System.out.println("auxArguments " + auxArguments);

        newTest.setArguments(auxArguments + newTest.getArguments());

        if (!task.isHasCodeTeacher()) {
            newTest.setOutput(" ");
        }

        task.getTests().add(newTest);
        taskbo.updateTask(task);

        InfoTest infoTest;
        if (idArgument == 0) {
            infoTest = new InfoTest(newTest.getTestId(), task.getTaskId(), idArgument);
        } else {
            //allArguments.add(newTest.getArguments());
            infoTest = new InfoTest(newTest.getTestId(), task.getTaskId(), idArgument, allArguments);
        }
        if (!RMIServices.isConnected()) {
            RMIServices.connectRMI();
        }

        RMIServices.securitySaveTest(infoTest);

            //RequestContext context = RequestContext.getCurrentInstance();
        //context.execute("newTestDialog.hide()");
        //context.execute("newTestDialogNoCode.hide()");
        newTest = new Test();
        submitDisabled = true;
        newTestError = "";
        newTestOutput = "";
        auxArguments = "";
        if (allArguments != null) {
            allArguments.clear();
        }
        idArgument = 99;
        this.getTest();

    }

    /**
     * Saves the arguments needed for execution
     */
    public void saveArgument() {
        allArguments = new ArrayList<String>();
        boolean saveArg = true;
        ArrayList<String> fileList = new ArrayList<String>();
        if (this.idArgument == 1) {

            System.out.println(newTest.getArguments());
        } else if (this.idArgument == 2) {
            InputStream docInputStream;
            if (docFile.getFileName().isEmpty()) {
                saveArg = false;
            } else {
                try {
                    docInputStream = docFile.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    System.out.println("YEEEEEEEEEEEEE" + fileList.get(0));
                    auxArguments = docFile.getFileName() + " ";
                    allArguments.add(fileList.get(0));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else if (this.idArgument == 3) {
            InputStream docInputStream;
            if (docFile.getFileName().isEmpty() || docFile2.getFileName().isEmpty()) {
                saveArg = false;
            } else {
                try {
                    docInputStream = docFile.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    docInputStream = docFile2.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    System.out.println(fileList.get(0) + "/n" + fileList.get(1));
                    auxArguments = docFile.getFileName() + " " + docFile2.getFileName() + " ";
                    allArguments.add(fileList.get(0));
                    allArguments.add(fileList.get(1));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else if (this.idArgument == 4) {
            InputStream docInputStream;
            if (docFile.getFileName().isEmpty() || docFile2.getFileName().isEmpty() || docFile3.getFileName().isEmpty()) {
                saveArg = false;
            } else {
                try {
                    docInputStream = docFile.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    docInputStream = docFile2.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    docInputStream = docFile3.getInputstream();
                    fileList.add(uploadFile(docInputStream));
                    System.out.println(fileList.get(0) + "/n" + fileList.get(1) + "/n" + fileList.get(2));
                    auxArguments = docFile.getFileName() + " " + docFile2.getFileName() + " " + docFile3.getFileName() + " ";
                    allArguments.add(fileList.get(0));
                    allArguments.add(fileList.get(1));
                    allArguments.add(fileList.get(2));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        allArguments.addAll(removeQuotes(newTest.getArguments()));
        if (saveArg) {
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "ARGUMENTS SAVED", "ARGUMENTS SAVED");
            FacesContext.getCurrentInstance().addMessage(null, msg);
        } else {
            FacesMessage msgerr = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error: You didn't select all files", "Error: You don't select all files");
            FacesContext.getCurrentInstance().addMessage(null, msgerr);
            newTest = new Test();
            submitDisabled = true;
            idArgument = 99;
        }
    }

    /**
     * Splits arguments by spaces.
     * If the input is: Hello "how are you" "im fine'
     * Will return 3 arguments: 'hello', 'how are you' and 'im fine'
     */
    public List<String> removeQuotes(String in){
        List<String> l = new ArrayList();
        java.util.regex.Pattern regex = java.util.regex.Pattern.compile("\"(\\\"|(?!\\\").)+\"|[^ ]+"); // [^\s"']+|"([^"]*)"|'([^']*)'
        java.util.regex.Matcher regexMatcher = regex.matcher(in);
        while (regexMatcher.find()) {
            l.add(regexMatcher.group().replace("\"",""));
        } 
        return l;
    }
    
    /**
     * Reset the arguments
     */
    public void cancelArgument() {
        newTest = new Test();
        submitDisabled = true;
        idArgument = 99;
    }

    /**
     * Reads the contents of file
     *
     * @param docInputStream
     * @return A type String, this String is the content of file.
     */
    public String uploadFile(InputStream docInputStream) {
        try {
            String fileArguments;
            StringWriter writer = new StringWriter();
            IOUtils.copy(docInputStream, writer, "UTF-8");
            fileArguments = writer.toString();
            return fileArguments;
        } catch (Exception x) {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, x.getClass().getName(), x.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, message);
            return null;
        }
    }

    public void cancelNewTest() {
        idArgument = 99;
        newTest = new Test();
        submitDisabled = true;
        newTestError = "";
        newTestOutput = "";
    }

    /**
     *
     */
    public void onArgumentsChanged() {
        RequestContext context = RequestContext.getCurrentInstance();
        //context.execute("newTestDialog.hide()");
        if (this.idArgument == 0) {
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "You select test without arguments.", "You select test without arguments.");
            FacesContext.getCurrentInstance().addMessage(null, msg);
            newTest.setArguments("");
        }
        if (this.idArgument == 1) {
            context.execute("newArguments.show()");
        }
        if (this.idArgument == 2) {
            context.execute("newArguments2.show()");
        }
        if (this.idArgument == 3) {
            context.execute("newArguments3.show()");
        }
        if (this.idArgument == 4) {
            context.execute("newArguments4.show()");
        }
    }

    /**
     * Redirect to the teacher view
     *
     * @return A type of String, This string indicates the instruction to
     * redirect.
     */
    public String goBack() {
        return "teacher?faces-redirect=true";
    }

    
    // ##########################################
    // ############# GETTERS & SETTERS ##########
    // ##########################################
    
    
    /**
     * Retrieve a list with the test.
     *
     * @return A List of Test type.
     */
    public void getTest() {
        testlist = taskbo.getTestsByTask(task);
    }

    public List<Test> getTestlist() {
        return testlist;
    }

    public void setTestlist(List<Test> testlist) {
        this.testlist = testlist;
    }

    public void setTestbo(TestBo testbo) {
        this.testbo = testbo;
    }

    public void setLoginbean(LoginBean loginbean) {
        this.loginbean = loginbean;
    }

    public Test getSelectedTest() {
        return selectedTest;
    }

    public void setSelectedTest(Test selectedTest) {
        this.selectedTest = selectedTest;
    }

    public Test getNewTest() {
        return newTest;
    }

    public void setNewTest(Test newTest) {
        this.newTest = newTest;
    }

    public boolean isSubmitDisabled() {
        return submitDisabled;
    }

    public void setSubmitDisabled(boolean submitDisabled) {
        this.submitDisabled = submitDisabled;
    }

    public String getNewTestOutput() {
        return newTestOutput;
    }

    public void setNewTestOutput(String newTestOutput) {
        this.newTestOutput = newTestOutput;
    }

    public String getNewTestError() {
        return newTestError;
    }

    public void setNewTestError(String newTestError) {
        this.newTestError = newTestError;
    }

    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    public int getIdArgument() {
        return idArgument;
    }

    public void setIdArgument(int idArgument) {
        this.idArgument = idArgument;
    }

    public UploadedFile getDocFile() {
        return docFile;
    }

    public void setDocFile(UploadedFile docFile) {
        this.docFile = docFile;
    }

    public UploadedFile getDocFile2() {
        return docFile2;
    }

    public void setDocFile2(UploadedFile docFile2) {
        this.docFile2 = docFile2;
    }

    public UploadedFile getDocFile3() {
        return docFile3;
    }

    public void setDocFile3(UploadedFile docFile3) {
        this.docFile3 = docFile3;
    }

    public boolean isNoStartDate() {
        return noStartDate;
    }

    public void setNoStartDate(boolean noStartDate) {
        this.noStartDate = noStartDate;
    }
}
