package pt.utl.ist.scripts.process.updateData;

import java.io.FileOutputStream;
import java.util.Set;

import net.sourceforge.fenixedu.domain.Enrolment;
import net.sourceforge.fenixedu.domain.IEnrolment;
import net.sourceforge.fenixedu.domain.StudentCurricularPlan;
import net.sourceforge.fenixedu.domain.student.Registration;
import net.sourceforge.fenixedu.domain.student.registrationStates.RegistrationStateType;
import net.sourceforge.fenixedu.domain.studentCurriculum.Credits;
import net.sourceforge.fenixedu.domain.studentCurriculum.Dismissal;
import net.sourceforge.fenixedu.domain.studentCurriculum.ExternalEnrolment;
import net.sourceforge.fenixedu.domain.studentCurriculum.Substitution;
import pt.ist.fenixframework.FenixFramework;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet.Row;
import pt.utl.ist.scripts.commons.AtomicScript;

public class UpdateCreditsClass extends AtomicScript {

    static final private String[] HEADERS = new String[] { "MOTIVO", "ALUNO", "MATRICULA", "CURSO", "CREDITS_ID", "SOURCES_IDS",
            "DISMISSALS_IDS" };
    final Spreadsheet creditsToSubstitution = new Spreadsheet("");
    final Spreadsheet creditsNotChanged = new Spreadsheet("");

    public static void main(String[] args) {
        process(new UpdateCreditsClass());
        System.exit(0);
    }

    @Override
    protected void run() throws Exception {
        for (final Credits credits : rootDomainObject.getCreditsSet()) {
            if (!credits.isSubstitution() && !credits.isEquivalence()) {
                if (hasAzoresSource(credits)) {
                    reportCredits(creditsToSubstitution.addRow(), "Origens dos Açores", credits);
                    changeToSubstitution(credits);
                } else if (hasErasmusSource(credits)) {
                    reportCredits(creditsToSubstitution.addRow(), "Origens de Erasmus", credits);
                    changeToSubstitution(credits);
                } else if (hasSameISTDegreeSource(credits)) {
                    reportCredits(creditsToSubstitution.addRow(), "Origens no mesmo curso", credits);
                    changeToSubstitution(credits);
                } else if (hasNotConcludedISTDegreeSource(credits)) {
                    reportCredits(creditsToSubstitution.addRow(), "Origens em Matrícula não concluída", credits);
                    changeToSubstitution(credits);
                } else {
                    reportCredits(creditsNotChanged.addRow(), "", credits);
                }
            }
        }

        creditsToSubstitution.setHeaders(HEADERS);
        creditsToSubstitution.exportToCSV(new FileOutputStream(UPDATE_DIR_PATH + "/creditsToSubstitution.csv"), "\t");
        creditsNotChanged.setHeaders(HEADERS);
        creditsNotChanged.exportToCSV(new FileOutputStream(UPDATE_DIR_PATH + "/creditsNotChanged.csv"), "\t");
    }

    private void reportCredits(final Row row, final String motive, final Credits credits) {
        row.setCell(motive);
        row.setCell(credits.getStudentCurricularPlan().getRegistration().getStudent().getNumber().toString());
        row.setCell(credits.getStudentCurricularPlan().getRegistration().getNumber().toString());
        row.setCell(credits.getStudentCurricularPlan().getRegistration().getDegree().getName());
        row.setCell(credits.getExternalId().toString());

        final StringBuilder sources = new StringBuilder();
        for (final IEnrolment enrolment : credits.getIEnrolments()) {
            sources.append(enrolment.getExternalId() + "|");
        }
        row.setCell(sources.toString());

        final StringBuilder dismissals = new StringBuilder();
        for (final Dismissal dismissal : credits.getDismissalsSet()) {
            dismissals.append(dismissal.getExternalId() + "|");
        }
        row.setCell(dismissals.toString());

        row.setCell(credits.getExecutionPeriod() == null ? "" : credits.getExecutionPeriod().getQualifiedName());
        row.setCell(credits.getGivenGrade());
    }

    private void changeToSubstitution(final Credits credits) {
        doAction(new ChangeToSubstitution(credits.getExternalId()));
    }

    private static class ChangeToSubstitution extends AtomicProcedure {

        private String creditsId;

        ChangeToSubstitution(final String creditsId) {
            this.creditsId = creditsId;
        }

        private Credits getCredits() {
            return FenixFramework.getDomainObject(this.creditsId);
        }

        @Override
        public void proc() throws Exception {

            final Credits credits = getCredits();

            final Substitution substitution = new Substitution();
            substitution.setStudentCurricularPlan(credits.getStudentCurricularPlan());
            substitution.getDismissals().addAll(credits.getDismissals());
            substitution.getEnrolments().addAll(credits.getEnrolments());
            substitution.setExecutionPeriod(credits.getExecutionPeriod());
            substitution.setGrade(credits.getGrade());

            credits.delete();
        }

    }

    private boolean hasAzoresSource(final Credits credits) {
        for (IEnrolment enrolment : credits.getIEnrolments()) {
            if (enrolment.isExternalEnrolment()) {
                ExternalEnrolment externalEnrolment = (ExternalEnrolment) enrolment;
                if (isAzoresExternalEnrolment(externalEnrolment)) {
                    return true;
                }
            }
        }

        return false;
    }

    private boolean isAzoresExternalEnrolment(ExternalEnrolment externalEnrolment) {
        return !externalEnrolment.getRegistration().isConcluded()
                && externalEnrolment.getExternalCurricularCourse().getFullPathName().indexOf("Açores") != -1;
    }

    private boolean hasErasmusSource(final Credits credits) {
        for (IEnrolment enrolment : credits.getIEnrolments()) {
            if (enrolment.isExternalEnrolment()) {
                ExternalEnrolment externalEnrolment = (ExternalEnrolment) enrolment;
                if (isErasmusExternalEnrolment(externalEnrolment, credits.getStudentCurricularPlan().getRegistration())) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isErasmusExternalEnrolment(ExternalEnrolment externalEnrolment, Registration registration) {
        if (externalEnrolment.getExecutionYear() != null) {
            Set<RegistrationStateType> registrationStates =
                    registration.getRegistrationStatesTypes(externalEnrolment.getExecutionYear());
            if (registrationStates.contains(RegistrationStateType.MOBILITY)) {
                return externalEnrolment.getExternalCurricularCourse().getFullPathName().indexOf("Portugal") == -1;
            }
        }
        return false;
    }

    private boolean hasSameISTDegreeSource(final Credits credits) {
        for (IEnrolment iEnrolment : credits.getIEnrolments()) {
            if (!iEnrolment.isExternalEnrolment()) {
                Enrolment enrolment = (Enrolment) iEnrolment;
                if (isSameISTDegree(enrolment, credits.getStudentCurricularPlan())) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isSameISTDegree(Enrolment enrolment, StudentCurricularPlan studentCurricularPlan) {
        return enrolment.getStudentCurricularPlan().getDegree() == studentCurricularPlan.getDegree();
    }

    private boolean hasNotConcludedISTDegreeSource(final Credits credits) {
        for (IEnrolment iEnrolment : credits.getIEnrolments()) {
            if (!iEnrolment.isExternalEnrolment()) {
                Enrolment enrolment = (Enrolment) iEnrolment;
                if (isNotConcludedISTDegree(enrolment)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isNotConcludedISTDegree(Enrolment enrolment) {
        return enrolment.getRegistration().getActiveStateType() != RegistrationStateType.CONCLUDED;
    }

}
