package pt.utl.ist.scripts.runOnce.space;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import net.sourceforge.fenixedu.domain.Lesson;
import net.sourceforge.fenixedu.domain.LessonInstance;
import net.sourceforge.fenixedu.domain.WrittenEvaluation;
import net.sourceforge.fenixedu.domain.resource.ResourceAllocation;
import net.sourceforge.fenixedu.domain.space.AllocatableSpace;
import net.sourceforge.fenixedu.domain.space.EventSpaceOccupation;
import net.sourceforge.fenixedu.domain.space.GenericEventSpaceOccupation;
import net.sourceforge.fenixedu.domain.space.LessonInstanceSpaceOccupation;
import net.sourceforge.fenixedu.domain.space.LessonSpaceOccupation;
import net.sourceforge.fenixedu.domain.space.Room;
import net.sourceforge.fenixedu.domain.space.WrittenEvaluationSpaceOccupation;
import net.sourceforge.fenixedu.util.HourMinuteSecond;

import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.YearMonthDay;

import pt.utl.ist.scripts.commons.AtomicScript;

public class ShowRoomScheduleInfo extends AtomicScript {

    private static final Interval INTERVAL = new Interval(new DateTime(2011, 02, 14, 0, 0, 0, 0), new DateTime(2011, 5, 28, 0, 0,
            0, 0));

    @Override
    protected void run() throws Exception {
        final List<AllocatableSpace> spaces = Room.getAllActiveAllocatableSpacesForEducationAndPunctualOccupations();
        for (final AllocatableSpace allocatableSpace : spaces) {
            run(allocatableSpace);
        }
        System.out.println("Done.");
    }

    private void run(final AllocatableSpace allocatableSpace) {
        /*for (final ResourceAllocation resourceAllocation : allocatableSpace.getResourceAllocationsSet()) {
            run(resourceAllocation);
        }*/
        LinkedList<EventSpaceOccupation> allocations = new LinkedList<EventSpaceOccupation>();
        for (final ResourceAllocation resourceAllocation : allocatableSpace.getResourceAllocationsSet()) {
            if (resourceAllocation instanceof EventSpaceOccupation) {
                final EventSpaceOccupation eventSpaceOccupation = (EventSpaceOccupation) resourceAllocation;
                allocations.add(eventSpaceOccupation);
            }
        }
        while (allocations.peek() != null) {
            EventSpaceOccupation alloc = allocations.pop();
            final OverlapInfo overlapInfo = overlaps(alloc, allocations);
            if (overlapInfo.isOverlaped()) {
                System.out.println(overlapInfo);
            }

        }
    }

    private class OverlapMatchInfo {
        public EventSpaceOccupation match;
        public Collection<Interval> intervals;

        public OverlapMatchInfo(EventSpaceOccupation match) {
            super();
            this.match = match;
            this.intervals = new HashSet<Interval>();
        }

        public EventSpaceOccupation getMatch() {
            return match;
        }

        public void setMatch(EventSpaceOccupation match) {
            this.match = match;
        }

        public Collection<Interval> getIntervals() {
            return intervals;
        }

        public void addInterval(Interval e) {
            intervals.add(e);
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("match: " + match.getRoom().getIdentification() + "\n");
            builder.append("intervals\n");
            for (Interval interval : intervals) {
                builder.append("\t " + interval + "\n");
            }
            return builder.toString();
        }

    }

    private class OverlapInfo {
        public EventSpaceOccupation needle;
        public Collection<OverlapMatchInfo> matches;

        public OverlapInfo(EventSpaceOccupation needle) {
            super();
            this.needle = needle;
            matches = new HashSet<OverlapMatchInfo>();
        }

        public void addOverlapMatchInfo(EventSpaceOccupation match, Interval interval) {
            boolean added = false;
            for (OverlapMatchInfo overlapMatchInfo : matches) {
                if (overlapMatchInfo.getMatch().equals(match)) {
                    overlapMatchInfo.addInterval(interval);
                    added = true;
                }
            }
            if (!added) {
                final OverlapMatchInfo overlapMatchInfo = new OverlapMatchInfo(match);
                overlapMatchInfo.addInterval(interval);
                matches.add(overlapMatchInfo);
            }
        }

        public void addOverlapMatchInfo(OverlapMatchInfo matchInfo) {
            matches.add(matchInfo);
        }

        public EventSpaceOccupation getNeedle() {
            return needle;
        }

        public Collection<OverlapMatchInfo> getMatches() {
            return matches;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("needle:" + needle.getRoom().getIdentification() + "\n");
            builder.append("needle.intervals:\n");
            final Collection<Interval> intervals = getIntervals(needle);
            for (Interval interval : intervals) {
                builder.append("\t " + interval + "\n");
            }
            for (OverlapMatchInfo match : matches) {
                builder.append(match);
            }
            return builder.toString();
        }

        public boolean isOverlaped() {
            return !matches.isEmpty();
        }
    }

    private OverlapInfo overlaps(EventSpaceOccupation needle, Collection<EventSpaceOccupation> haystack) {
        final Collection<Interval> needleIntervals = getIntervals(needle);
        final OverlapInfo overlapInfo = new OverlapInfo(needle);
        for (EventSpaceOccupation eventSpaceOccupation : haystack) {
            final Collection<Interval> intervals = getIntervals(eventSpaceOccupation);
            for (Interval needleinterval : needleIntervals) {
                if (INTERVAL.overlaps(needleinterval)) {
                    for (Interval interval : intervals) {
                        if (INTERVAL.overlaps(interval) && needleinterval.overlaps(interval)) {
                            overlapInfo.addOverlapMatchInfo(eventSpaceOccupation, interval);
                        }
                    }
                }

            }
        }
        return overlapInfo;
    }

    private void dumpOccupationInfo(final EventSpaceOccupation occupation) {
        System.out.println(occupation.getRoom().getIdentification());
        for (Interval interval : getIntervals(occupation)) {
            System.out.printf("\t%s\n", interval);
        }
    }

    private void run(final ResourceAllocation resourceAllocation) {
        if (resourceAllocation instanceof EventSpaceOccupation) {
            final EventSpaceOccupation eventSpaceOccupation = (EventSpaceOccupation) resourceAllocation;
            run(eventSpaceOccupation);
        }
    }

    private DateTime toDateTime(final YearMonthDay yearMonthDay, final HourMinuteSecond hourMinuteSecond) {
        return new DateTime(yearMonthDay.getYear(), yearMonthDay.getMonthOfYear(), yearMonthDay.getDayOfMonth(),
                hourMinuteSecond.getHour(), hourMinuteSecond.getMinuteOfHour(), hourMinuteSecond.getSecondOfMinute(), 0);
    }

    private Interval toInterval(final EventSpaceOccupation eventSpaceOccupation) {
        final YearMonthDay begin = eventSpaceOccupation.getBeginDate();
        final HourMinuteSecond beginTime = eventSpaceOccupation.getStartTimeDateHourMinuteSecond();
        final YearMonthDay end = eventSpaceOccupation.getEndDate();
        final HourMinuteSecond endTime = eventSpaceOccupation.getEndTimeDateHourMinuteSecond();
        return new Interval(toDateTime(begin, beginTime), toDateTime(end, endTime));
    }

    private Collection<Interval> getIntervals(final EventSpaceOccupation eventSpaceOccupation) {
        if (eventSpaceOccupation instanceof GenericEventSpaceOccupation) {
            final GenericEventSpaceOccupation genericEventSpaceOccupation = (GenericEventSpaceOccupation) eventSpaceOccupation;
            return getIntervals(genericEventSpaceOccupation);
        } else if (eventSpaceOccupation instanceof LessonInstanceSpaceOccupation) {
            final LessonInstanceSpaceOccupation lessonInstanceSpaceOccupation =
                    (LessonInstanceSpaceOccupation) eventSpaceOccupation;
            return getIntervals(lessonInstanceSpaceOccupation);
        } else if (eventSpaceOccupation instanceof LessonSpaceOccupation) {
            final LessonSpaceOccupation lessonSpaceOccupation = (LessonSpaceOccupation) eventSpaceOccupation;
            return getIntervals(lessonSpaceOccupation);
        } else if (eventSpaceOccupation instanceof WrittenEvaluationSpaceOccupation) {
            final WrittenEvaluationSpaceOccupation writtenEvaluationSpaceOccupation =
                    (WrittenEvaluationSpaceOccupation) eventSpaceOccupation;
            return getIntervals(writtenEvaluationSpaceOccupation);
        } else {
            throw new Error();
        }
    }

    private Collection<Interval> getIntervals(final GenericEventSpaceOccupation genericEventSpaceOccupation) {
        return genericEventSpaceOccupation.getEventSpaceOccupationIntervals(INTERVAL.getStart(), INTERVAL.getEnd());
        //return Collections.singletonList(toInterval(genericEventSpaceOccupation));
    }

    private Collection<Interval> getIntervals(final LessonInstanceSpaceOccupation lessonInstanceSpaceOccupation) {
        final Collection<Interval> intervals = new HashSet<Interval>();
        for (final LessonInstance lessonInstance : lessonInstanceSpaceOccupation.getLessonInstancesSet()) {
            intervals.add(new Interval(lessonInstance.getBeginDateTime(), lessonInstance.getEndDateTime()));

        }
        return intervals;
    }

    private Collection<Interval> getIntervals(final LessonSpaceOccupation lessonSpaceOccupation) {
        final Collection<Interval> intervals = new HashSet<Interval>();
        final Lesson lesson = lessonSpaceOccupation.getLesson();
        for (final YearMonthDay yearMonthDay : lesson.getAllLessonDatesWithoutInstanceDates()) {
            final Interval interval =
                    new Interval(toDateTime(yearMonthDay, lesson.getBeginHourMinuteSecond()), toDateTime(yearMonthDay,
                            lesson.getEndHourMinuteSecond()));
            intervals.add(interval);
//	    if (INTERVAL.overlap(interval) != null) {
//		System.out.println(lessonSpaceOccupation.getRoom().getIdentification() + " " + lessonSpaceOccupation.getClass().getSimpleName() + ": " + interval);
//		final Shift shift = lesson.getShift();
//		final ExecutionCourse executionCourse = shift.getExecutionCourse();
//		final ExecutionSemester executionSemester = executionCourse.getExecutionPeriod();
//		final ExecutionYear executionYear = executionSemester.getExecutionYear();
//		System.out.println(executionYear.getYear() + " - " + executionSemester.getSemester()
//			+ " - " + " " + executionCourse.getNome() + " - " + shift.getNome());
//		for (final SchoolClass schoolClass : shift.getAssociatedClassesSet()) {
//		    System.out.println("   " + schoolClass.getNome() + " - " + schoolClass.getExecutionDegree().getDegreeName());
//		}
//	    }
        }
        return intervals;
    }

    private Collection<Interval> getIntervals(final WrittenEvaluationSpaceOccupation writtenEvaluationSpaceOccupation) {
        final Collection<Interval> intervals = new HashSet<Interval>();
        for (final WrittenEvaluation writtenEvaluation : writtenEvaluationSpaceOccupation.getWrittenEvaluationsSet()) {
            final Interval interval = new Interval(writtenEvaluation.getBeginningDateTime(), writtenEvaluation.getEndDateTime());
            intervals.add(interval);
//	    if (INTERVAL.overlap(interval) != null) {
//		System.out.println(writtenEvaluationSpaceOccupation.getRoom().getIdentification() + " " + writtenEvaluationSpaceOccupation.getClass().getSimpleName() + ": " + interval);
//	    }
        }
        return intervals;
    }

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

}
