/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the
 * License at
 * http://www.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */
package com.icesoft.faces.facelets.component.schedule.week;

import com.icesoft.faces.facelets.component.schedule.common.CalendarEventBean;
import com.icesoft.faces.facelets.component.schedule.day.DayModel;
import com.icesoft.faces.facelets.component.schedule.day.DaySlot;

import java.io.Serializable;
import java.util.*;

/**
 * The WeekModel is designed to aid the UI in building week views.  This
 * class is pretty simple in that is basically just delegates to the day
 * model for building its data structure.
 * <p/>
 *
 * @author ICEfaces Technologies Inc.
 */
public class WeekModel implements Serializable {

    private List days;

    private Calendar currentCalendar;

    private int maxSlotSize = 1;

    public WeekModel(Calendar calendar) {

        currentCalendar = calendar;

        // create array to contain the 7 days of the week
        days = new ArrayList(7);

        // figure out the starting day of the week for the given cursor
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);

        // Monday as a day in the month
        int weekDayOfMonthStart;
        if (dayOfWeek != Calendar.SUNDAY) {
            weekDayOfMonthStart = day - (dayOfWeek - Calendar.MONDAY);
        } else {
            weekDayOfMonthStart = day - 6;
        }
        // update the currentCalendar to start of week, Monday
        currentCalendar.set(Calendar.DAY_OF_MONTH, weekDayOfMonthStart);
        // temporary calendar holding the day at the end of the week
        Calendar tempCalendar = new GregorianCalendar(currentCalendar.get(Calendar.YEAR),
                currentCalendar.get(Calendar.MONTH),
                currentCalendar.get(Calendar.DAY_OF_MONTH));
        tempCalendar.add(Calendar.DATE, 6);
        // First check if the week runs in to the next month
        if (currentCalendar.get(Calendar.MONTH) != tempCalendar.get(Calendar.MONTH)) {
            // Week days run in to next month
            // create day objects for each day of current month
            for (int i = weekDayOfMonthStart, max = currentCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i <= max; i++) {
                days.add(new DayModel(year, month, i));
            }
            // create day objects for each day of next month
            for (int i = 1, max = tempCalendar.get(Calendar.DAY_OF_MONTH); i <= max; i++) {
                days.add(new DayModel(year, tempCalendar.get(Calendar.MONTH), i));
            }
        } else {
            // create day objects for each day, they are all in the current month
            for (int i = weekDayOfMonthStart, max = i + 7; i < max; i++) {
                days.add(new DayModel(year, month, i));
            }
        }

        // add spacers to fill out calendar boxes.
        topUpMonthSpacerSlots(maxSlotSize);
    }

    /**
     * Utility to add a default number of empty slots to the events.  We
     * want to keep the slot count the same so the UI layout is balanced.
     *
     * @param numberOfSlots total number of slots to have in each days event
     *                      slots.
     */
    private void topUpMonthSpacerSlots(int numberOfSlots) {
        DaySlot allDaySlot;
        for (Object day : days) {
            DayModel dayModel = (DayModel) day;
            // first slot is always empty
            allDaySlot = dayModel.getAllDaySlots();
            if (allDaySlot != null) {
                if (allDaySlot.getCalEvents().size() < numberOfSlots) {
                    for (int i = 0, max = numberOfSlots - allDaySlot.getCalEvents().size();
                         i < max; i++) {
                        allDaySlot.getCalEvents().add(new CalendarEventBean());
                    }
                }
            }
        }
    }

    /**
     * Utility to update the total number of slot spacers if a new
     * event has been added to the week and more specifically a day slot.
     *
     * @param daySlot slot to check size of.
     */
    private void topUpMonthSpacerSlots(DaySlot daySlot) {
        int currentSlotSize = daySlot.getCalEvents().size();
        // if we are bigger than the max then we have to do the update.
        if (currentSlotSize > maxSlotSize) {
            maxSlotSize = currentSlotSize;
            topUpMonthSpacerSlots(maxSlotSize);
        }
    }

    /**
     * Gets the 12ish months associated with this year.
     *
     * @return list of month models for the year.
     */
    public List getDays() {
        return days;
    }

    /**
     * Adds the given calEvent to the DayModel. This event will only add events
     * that fall in the year specified by the class.
     *
     * @param calEvent event to add to the model.
     */
    public void addEvent(CalendarEventBean calEvent) {
        // iterate over range adding each even to the respective days.
        int currentMonth = currentCalendar.get(Calendar.MONTH);
        int currentYear = currentCalendar.get(Calendar.YEAR);
        int currentDay;
        // check year,
        if (currentYear == calEvent.getStart().get(Calendar.YEAR) ||
                currentYear == calEvent.getEnd().get(Calendar.YEAR)) {
            // check month, this is a week view, so need next month's events
            // when viewing a week that goes into the next month
            if (currentMonth == calEvent.getStart().get(Calendar.MONTH) ||
                    currentMonth == calEvent.getEnd().get(Calendar.MONTH) ||
                    currentMonth + 1 == calEvent.getStart().get(Calendar.MONTH) ||
                    currentMonth + 1 == calEvent.getEnd().get(Calendar.MONTH)) {

                // Ignore events that start after the last day of this week
                Calendar lastDay = ((DayModel) days.get(6)).getCurrentDay();
                // Temporary calendar holding day after this week
                Calendar tempCalendar = new GregorianCalendar(lastDay.get(Calendar.YEAR), lastDay.get(Calendar.MONTH), lastDay.get(Calendar.DAY_OF_MONTH));
                tempCalendar.set(Calendar.HOUR_OF_DAY, 0);
                tempCalendar.set(Calendar.MINUTE, 0);
                tempCalendar.set(Calendar.SECOND, 0);
                tempCalendar.set(Calendar.MILLISECOND, 0);
                tempCalendar.add(Calendar.DATE, 1);
                if (calEvent.getStart().getTimeInMillis() < tempCalendar.getTimeInMillis()) {

                    // get insert slot of first day
                    int slot = 0;
                    // finally add day events to correct day models.
                    for (Object day : days) {
                        DayModel dayModel = (DayModel) day;
                        currentDay = dayModel.getDay();
                        // get slot of first day so it can be used for multi-day events.
                        if (calEvent.isAllDay()) {
                            slot = dayModel.getAllDaySlots().getFirstFreeSlot();
                        }
                        // add the events.
                        dayModel.addEvent(new CalendarEventBean(calEvent), slot, true);

                        // top up spacers for all day events.
                        if (calEvent.isAllDay()) {
                            topUpMonthSpacerSlots(dayModel.getAllDaySlots());
                        }
                    }
                }
            }
        }

    }

}