package com.alexstyl.specialdates.upcoming;

import com.alexstyl.specialdates.Optional;
import com.alexstyl.specialdates.events.bankholidays.BankHoliday;
import com.alexstyl.specialdates.date.CelebrationDate;
import com.alexstyl.specialdates.date.ContactEvent;
import com.alexstyl.specialdates.date.Date;
import com.alexstyl.specialdates.events.ContactEvents;
import com.alexstyl.specialdates.date.DayDate;
import com.alexstyl.specialdates.events.namedays.NamesInADate;
import com.alexstyl.specialdates.util.HashMapList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

public class UpcomingDatesBuilder {

    private static final List<ContactEvent> NO_CONTACT_EVENTS = Collections.unmodifiableList(new ArrayList<ContactEvent>());

    private final HashMapList<Date, ContactEvent> contactEvents = new HashMapList<>();
    private final HashMap<Date, NamesInADate> namedays = new HashMap<>();
    private final HashMap<Date, BankHoliday> bankHolidays = new HashMap<>();

    private Optional<DayDate> earliestDate = Optional.absent();
    private Optional<DayDate> latestDate = Optional.absent();

    public UpcomingDatesBuilder withContactEvents(List<ContactEvent> contactEvents) {
        for (ContactEvent contactEvent : contactEvents) {
            DayDate date = contactEvent.getDate();
            keepIfEarliestDate(date);
            keepIfLatestDate(date);
            this.contactEvents.addValue(date, contactEvent);
        }
        return this;
    }

    private void keepIfLatestDate(DayDate date) {
        if (latestDate.isPresent()) {
            DayDate previousLatestDate = latestDate.get();
            if (date.isAfter(previousLatestDate)) {
                latestDate = new Optional<>(date);
            }
        } else {
            latestDate = new Optional<>(date);
        }
    }

    private void keepIfEarliestDate(DayDate date) {
        if (earliestDate.isPresent()) {
            DayDate previousEarliestDate = earliestDate.get();
            if (date.isBefore(previousEarliestDate)) {
                earliestDate = new Optional<>(date);
            }
        } else {
            earliestDate = new Optional<>(date);
        }
    }

    public UpcomingDatesBuilder withNamedays(List<NamesInADate> namedays) {
        for (NamesInADate nameday : namedays) {
            DayDate date = nameday.getDate();
            keepIfEarliestDate(date);
            keepIfLatestDate(date);
            this.namedays.put(date, nameday);
        }
        return this;
    }

    public UpcomingDatesBuilder withBankHolidays(List<BankHoliday> bankHolidays) {
        for (BankHoliday bankHoliday : bankHolidays) {
            DayDate date = bankHoliday.getDate();
            keepIfEarliestDate(date);
            keepIfLatestDate(date);
            this.bankHolidays.put(date, bankHoliday);
        }
        return this;
    }

    public List<CelebrationDate> build() {
        List<CelebrationDate> celebrationDates = new ArrayList<>();
        if (noEventsArePresent()) {
            return celebrationDates;
        }
        DayDate indexDate = earliestDate.get();
        DayDate lastDate = latestDate.get();
        while (indexDate.isBefore(lastDate)) {
            List<ContactEvent> contactEvent = contactEvents.get(indexDate);
            if (contactEvent == null) {
                contactEvent = NO_CONTACT_EVENTS;
            }
            NamesInADate namedays = this.namedays.get(indexDate);

            BankHoliday bankHoliday = bankHolidays.get(indexDate);
            if (atLeastOneEventExists(contactEvent, namedays, bankHoliday)) {
                CelebrationDate date = new CelebrationDate(
                        indexDate,
                        ContactEvents.createFrom(indexDate, contactEvent),
                        new Optional<>(namedays),
                        new Optional<>(bankHoliday)
                );
                celebrationDates.add(date);
            }
            indexDate = indexDate.addDay(1);
        }
        return celebrationDates;
    }

    private boolean noEventsArePresent() {
        return !earliestDate.isPresent() || !latestDate.isPresent();
    }

    private boolean atLeastOneEventExists(List<ContactEvent> contactEvent, NamesInADate namedays, BankHoliday bankHoliday) {
        return contactEvent.size() > 0 || namedays != null || bankHoliday != null;
    }
}

@@ -2912,21 +2912,35 @@
 %7D%0A%0A    p
-ublic
+rivate static final
  List%3CCe
@@ -2958,168 +2958,245 @@
 te%3E 
-build() %7B%0A        List%3CCelebrationDate%3E celebrationDates = new ArrayList%3C%3E();%0A        if (noEventsArePresent()) %7B%0A            return celebrationDates;%0A        %7D
+NO_CELEBRATIONS = Collections.emptyList();%0A%0A    public List%3CCelebrationDate%3E build() %7B%0A        if (noEventsArePresent()) %7B%0A            return NO_CELEBRATIONS;%0A        %7D%0A%0A        List%3CCelebrationDate%3E celebrationDates = new ArrayList%3C%3E();
 %0A   
@@ -3310,24 +3310,25 @@
 dexDate.
-isBefore
+compareTo
 (lastDat
@@ -3325,24 +3325,29 @@
 To(lastDate)
+ %3C= 0
 ) %7B%0A        

Check if index date is before or same as the lastDate