/*
 * 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.day;

import com.icesoft.faces.facelets.component.names.BeanNames;
import com.icesoft.faces.facelets.component.schedule.common.CalendarEventBean;
import com.icesoft.faces.facelets.component.util.CalendarViewUtil;
import com.icesoft.faces.facelets.component.util.Util;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import java.io.Serializable;
import java.util.Calendar;

/**
 * The day model is responsible for taking the Calendar Event and assembling
 * a UI data structure to show day related views.
 * <p/>
 *
 * @author ICEfaces Technologies Inc.
 */
public class DayModel implements Serializable {

    private DaySlot[] timeSlots;

    private DaySlot allDaySlots;

    private Calendar currentDay;
    private int year;
    private int month;
    private int day;

    /**
     * Constructs a new Day model.
     *
     * @param day calendar day.
     */
    public DayModel(Calendar day) {
        this(day.get(Calendar.YEAR),
                day.get(Calendar.MONTH),
                day.get(Calendar.DAY_OF_MONTH));
    }

    /**
     * Constructs a new Day model.
     *
     * @param year
     * @param month
     * @param day
     */
    public DayModel(final int year, final int month, final int day) {
        this.year = year;
        this.month = month;
        this.day = day;

        CalendarViewUtil cvu = (CalendarViewUtil) Util.getManagedBean(BeanNames.CALENDAR_VIEW_UTIL);
        currentDay = cvu.getLocalizedCalendar();
        // Three 0's for hour, minute and seconds to set to beginning of day
        currentDay.set(year, month, day, 0, 0, 0);
        currentDay.set(Calendar.MILLISECOND, 0);

        // two slots for each hour
        timeSlots = new DaySlot[cvu.getHoursInDay() * 2];

        // only one slot for all day
        allDaySlots = new DaySlot(year, month, day, 0, 0);

        // create empty slots of hours in a day
        for (int i = 0, h = cvu.getStartHour(), m = 0; i < cvu.getHoursInDay() * 2; i++) {
            timeSlots[i] = new DaySlot(year, month, day, h, m);
            if (i % 2 != 0) {
                h++;
                m = 0;
            } else {
                m = 30;
            }
        }
    }

    /**
     * Adds a new Event to the Day Model.  This method does a lot of the
     * lifting for the class.  Because an event can span more than one day this
     * method is responsible for putting events into each of the days that it
     * covers.  The makes UI construction a lot easier.
     *
     * @param calEvent calEvent to add to the month.
     * @param slot     slot for allday event to insert event into. ignored for
     *                 regular events.
     * @param isFromWeekModel
     */
    public void addEvent(CalendarEventBean calEvent, int slot, boolean isFromWeekModel) {

        double startDec = calEvent.getStart().get(Calendar.MONTH) +
                calEvent.getStart().get(Calendar.DAY_OF_MONTH) / 100.0;
        double endDec = calEvent.getEnd().get(Calendar.MONTH) +
                calEvent.getEnd().get(Calendar.DAY_OF_MONTH) / 100.0;

        double dayDec = month + day / 100.0;

        if (isFromWeekModel && calEvent.isAllDay()) {
            // Different algorithm when called from WeekModel as Days can go in to the next month
            if (!(currentDay.getTimeInMillis() >= calEvent.getStart().getTimeInMillis() && currentDay.getTimeInMillis() <= calEvent.getEnd().getTimeInMillis())) {
                return;
            }
        } else if (!(dayDec >= startDec && dayDec <= endDec)) {
            return;
        }

        // assign a single event to one or more slots.
        if (calEvent.isAllDay()) {
            allDaySlots.addEvent(calEvent, slot);
        } else {
            Calendar startDate = calEvent.getStart();
            Calendar endDate = calEvent.getEnd();

            // time info
            int startHour = startDate.get(Calendar.HOUR_OF_DAY);
            int endHour = endDate.get(Calendar.HOUR_OF_DAY);
            int startMin = startDate.get(Calendar.MINUTE);
            int endMin = endDate.get(Calendar.MINUTE);

            // calculate which of the 16 slots to add to event to.
            CalendarViewUtil cvu = (CalendarViewUtil) Util.getManagedBean(BeanNames.CALENDAR_VIEW_UTIL);
            int startSlot = (startHour - cvu.getStartHour()) * 2 + Math.round(startMin / 60.0f);
            int endSlot = (endHour - cvu.getStartHour()) * 2 + Math.round(endMin / 60.0f);
            // add event to necessary slots.
            boolean eventOutsideBusinessHours = false;
            for (int i = startSlot; i < endSlot; i++) {
                slot = 0;//timeSlots[i].getFirstFreeSlot();
                try {
                    timeSlots[i].addEvent(new CalendarEventBean(calEvent), slot);
                } catch (ArrayIndexOutOfBoundsException e) {
                    // An ArrayIndexOutOfBoundsException means we have an event
                    // time outside the calendar's visible hours.
                    eventOutsideBusinessHours = true;
                }
            }
            if (eventOutsideBusinessHours) {
                // Display message in UI suggesting they expand viewable hours.
                String message = "There is an event outside the visible hours.  Select 24hr to view this event.";
                FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_INFO, message, "");
                FacesContext facesContext = FacesContext.getCurrentInstance();
                facesContext.addMessage("viewableHoursCheckbox", facesMessage);
            }
        }
    }

    public DaySlot getAllDaySlots() {
        return allDaySlots;
    }

    public DaySlot[] getTimeSlots() {
        return timeSlots;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public int getNumberSlots() {
        return timeSlots.length;
    }

    public Calendar getCurrentDay() {
        return currentDay;
    }
}