001/**
002 * Copyright 2004-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.kpme.tklm.time.calendar;
017
018import org.apache.log4j.Logger;
019import org.joda.time.DateTime;
020import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
021import org.kuali.kpme.core.api.earncode.EarnCode;
022import org.kuali.kpme.core.calendar.CalendarParent;
023import org.kuali.kpme.core.calendar.web.CalendarDay;
024import org.kuali.kpme.core.calendar.web.CalendarWeek;
025import org.kuali.kpme.core.service.HrServiceLocator;
026import org.kuali.kpme.core.util.HrConstants;
027import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
028import org.kuali.kpme.tklm.api.time.calendar.TkCalendarContract;
029import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
030import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
031import org.kuali.kpme.tklm.leave.block.LeaveBlockRenderer;
032import org.kuali.kpme.tklm.time.timeblock.TimeBlockBo;
033import org.kuali.kpme.tklm.time.timeblock.web.TimeBlockRenderer;
034import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetailRenderer;
035import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;
036
037import java.util.ArrayList;
038import java.util.List;
039import java.util.Map;
040
041public class TkCalendar extends CalendarParent implements TkCalendarContract {
042
043        private static final long serialVersionUID = -5393478597803080619L;
044        private static final Logger LOG = Logger.getLogger(TkCalendar.class);
045    private CalendarEntry payCalEntry;
046    private DateTime beginDateTime;
047    private DateTime endDateTime;
048
049    public TkCalendar() {}
050
051    public TkCalendar(CalendarEntry calendarEntry) {
052        super(calendarEntry);
053    }
054
055    public static TkCalendar getCalendar(TkTimeBlockAggregate tbAggregate, LeaveBlockAggregate lbAggregate) {
056        TkCalendar tc = new TkCalendar();
057         
058        if (tbAggregate != null && lbAggregate != null) {
059                if(tbAggregate.getDayTimeBlockList().size() != lbAggregate.getDayLeaveBlockList().size()){
060                        LOG.error("TimeBlockAggregate and LeaveBlockAggregate should have the same size of Day Blocks List");
061                        return null;
062//                      throw new RuntimeException("TimeBlockAggregate and LeaveBlockAggregate should have the same size of Day Blocks List");
063                } 
064                        
065                List<CalendarWeek> weeks = new ArrayList<CalendarWeek>();
066                tc.setPayCalEntry(tbAggregate.getPayCalendarEntry());
067        
068                 int firstDay = 0;
069                 if (tc.getBeginDateTime().getDayOfWeek() != 7) {
070                     firstDay = 0 - tc.getBeginDateTime().getDayOfWeek();   // always render calendar weeks from Sundays
071                 }
072                 for (int i = 0; i < tbAggregate.numberOfAggregatedWeeks(); i++) {
073                     TkCalendarWeek week = new TkCalendarWeek();
074                     List<List<TimeBlock>> weekBlocks = tbAggregate.getWeekTimeBlocks(i);
075                     List<List<LeaveBlock>> weekLeaveBlocks = lbAggregate.getWeekLeaveBlocks(i);
076                     List<CalendarDay> days = new ArrayList<CalendarDay>(7);
077        
078                     for (int j = 0; j < weekBlocks.size(); j++) {
079                         List<TimeBlock> dayBlocks = weekBlocks.get(j);
080                         List<LeaveBlock> dayLeaveBlocks = weekLeaveBlocks.get(j);
081                         // Create the individual days.
082                         TkCalendarDay day = new TkCalendarDay();
083                         
084                       //Set missed punch flag
085                         int k=0;
086                         for(TimeBlock tb : dayBlocks){
087                                TimeBlockBo timeBlockBo = TimeBlockBo.from(tb);
088                                timeBlockBo.assignClockedByMissedPunch();
089                                tb = TimeBlockBo.to(timeBlockBo);
090                                dayBlocks.set(k, tb);
091                                k++;
092                                 }
093                            
094                         day.setTimeblocks(dayBlocks);
095                         day.setLeaveBlocks(dayLeaveBlocks);
096                         day.setDayNumberString(tc.getDayNumberString(i * 7 + j + firstDay));
097                         day.setDayNumberDelta(i * 7 + j + firstDay);
098                         day.setDateString(tc.getDateString(day.getDayNumberDelta()));
099                         assignDayLunchLabel(day);
100                         int dayIndex = i * 7 + j + firstDay;
101                         DateTime beginDateTemp = tc.getBeginDateTime().plusDays(dayIndex);
102                         day.setGray(false);
103                         if (beginDateTemp.isBefore(tc.getBeginDateTime().getMillis())
104                                 || beginDateTemp.isAfter(tc.getEndDateTime().getMillis())) {
105                             day.setGray(true);
106                         }
107                         if (tc.getEndDateTime().getHourOfDay() == 0 && beginDateTemp.equals(tc.getEndDateTime())) {
108                             day.setGray(true);
109                         }
110                         days.add(day);
111                     }
112                     week.setDays(days);
113                     weeks.add(week);
114                 }
115                 tc.setWeeks(weeks);
116             } else {
117                 return null;
118             }
119
120        return tc;
121    }
122    
123    public static TkCalendar getCalendar(TkTimeBlockAggregate aggregate) {
124        TkCalendar tc = new TkCalendar();
125
126        if (aggregate != null) {
127            List<CalendarWeek> weeks = new ArrayList<CalendarWeek>();
128            tc.setPayCalEntry(aggregate.getPayCalendarEntry());
129
130            int firstDay = 0;
131            if (tc.getBeginDateTime().getDayOfWeek() != 7) {
132                firstDay = 0 - tc.getBeginDateTime().getDayOfWeek();   // always render calendar weeks from Sundays
133            }
134            for (int i = 0; i < aggregate.numberOfAggregatedWeeks(); i++) {
135                TkCalendarWeek week = new TkCalendarWeek();
136                List<List<TimeBlock>> weekBlocks = aggregate.getWeekTimeBlocks(i);
137                List<CalendarDay> days = new ArrayList<CalendarDay>(7);
138
139                for (int j = 0; j < weekBlocks.size(); j++) {
140                    List<TimeBlock> dayBlocks = weekBlocks.get(j);
141                    // Create the individual days.
142                    TkCalendarDay day = new TkCalendarDay();
143                    
144                    day.setTimeblocks(dayBlocks);
145                    day.setDayNumberString(tc.getDayNumberString(i * 7 + j + firstDay));
146                    day.setDayNumberDelta(i * 7 + j + firstDay);
147                    day.setDateString(tc.getDateString(day.getDayNumberDelta()));
148                    assignDayLunchLabel(day);
149                    int dayIndex = i * 7 + j + firstDay;
150                    DateTime beginDateTemp = tc.getBeginDateTime().plusDays(dayIndex);
151                    day.setGray(false);
152                    if (beginDateTemp.isBefore(tc.getBeginDateTime().getMillis())
153                            || beginDateTemp.isAfter(tc.getEndDateTime().getMillis())) {
154                        day.setGray(true);
155                    }
156                    if (tc.getEndDateTime().getHourOfDay() == 0 && beginDateTemp.equals(tc.getEndDateTime())) {
157                        day.setGray(true);
158                    }
159                    days.add(day);
160                }
161                week.setDays(days);
162                weeks.add(week);
163            }
164            tc.setWeeks(weeks);
165        } else {
166            return null;
167        }
168
169        return tc;
170    }
171
172    public static void assignDayLunchLabel(TkCalendarDay day) {
173        EarnCode ec = null;
174        String label = "";
175        String id = "";
176        for (TimeBlockRenderer tbr : day.getBlockRenderers()) {
177            for (TimeHourDetailRenderer thdr : tbr.getDetailRenderers()) {
178                if (thdr.getTitle().equals(HrConstants.LUNCH_EARN_CODE)) {
179                    ec = HrServiceLocator.getEarnCodeService().getEarnCode(thdr.getTitle(), tbr.getTimeBlock().getBeginDateTime().toLocalDate());
180                    if (ec != null) {
181                        label = ec.getDescription() + " : " + thdr.getHours() + " hours";
182                        id = thdr.getTkTimeHourDetailId();
183                    }
184                }
185            }
186            tbr.setLunchLabel(label);
187            tbr.setLunchLabelId(id);
188
189            label = "";
190        }
191    }
192
193    
194    public void assignAssignmentStyle(Map<String, String> styleMap) {
195        for (CalendarWeek aWeek : this.getWeeks()) {
196            for (CalendarDay aDay : aWeek.getDays()) {
197                for (TimeBlockRenderer tbr : ((TkCalendarDay)aDay).getBlockRenderers()) {
198                    String assignmentKey = tbr.getTimeBlock().getAssignmentKey();
199                    if (assignmentKey != null && styleMap.containsKey(assignmentKey)) {
200                        tbr.setAssignmentClass(styleMap.get(assignmentKey));
201                    } else {
202                        tbr.setAssignmentClass("");
203                    }
204                }
205                for (LeaveBlockRenderer lbr : ((TkCalendarDay)aDay).getLeaveBlockRenderers()) {
206                    String assignmentKey = lbr.getLeaveBlock().getAssignmentKey();
207                    if (assignmentKey != null && styleMap.containsKey(assignmentKey)) {
208                        lbr.setAssignmentClass(styleMap.get(assignmentKey));
209                    } else {
210                        lbr.setAssignmentClass("");
211                    }
212                } 
213                
214            }
215        }
216    }
217
218    public CalendarEntry getPayCalEntry() {
219        return payCalEntry;
220    }
221
222    public void setPayCalEntry(CalendarEntry payCalEntry) {
223        this.payCalEntry = payCalEntry;
224        // Relative time, with time zone added.
225        this.beginDateTime = payCalEntry.getBeginPeriodLocalDateTime().toDateTime(HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
226        this.endDateTime = payCalEntry.getEndPeriodLocalDateTime().toDateTime(HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
227    }
228
229    public DateTime getBeginDateTime() {
230        return beginDateTime;
231    }
232
233    public DateTime getEndDateTime() {
234        return endDateTime;
235    }
236
237    /**
238     * Provides the calendar title / heading. If the Pay Calendar entry spans
239     * multiple months, you get Abbreviated Month name + year of both the
240     * beginning month and the ending month.
241     *
242     * @return String for calendar title use.
243     */
244    public String getCalendarTitle() {
245        StringBuilder sb = new StringBuilder();
246
247        if (getBeginDateTime().getMonthOfYear() == getEndDateTime().getMonthOfYear() ||
248                (getBeginDateTime().getMonthOfYear() != getEndDateTime().getMonthOfYear()
249                        && getEndDateTime().getDayOfMonth() == 1 && getEndDateTime().getSecondOfDay() == 0)) {
250            sb.append(getBeginDateTime().toString("MMMM y"));
251        } else {
252            sb.append(getBeginDateTime().toString("MMM y"));
253            sb.append(" - ");
254            sb.append(getEndDateTime().toString("MMM y"));
255        }
256
257        return sb.toString();
258    }
259
260    /**
261     * Assumption of 7 "days" per week, or 7 "blocks" per row.
262     *
263     * @return A list of string titles for each row block (day)
264     */
265    public List<String> getCalendarDayHeadings() {
266        List<String> dayStrings = new ArrayList<String>(7);
267        // always render from Sunday
268        int firstDay = 0 - getBeginDateTime().getDayOfWeek();
269        int lastDay = firstDay + 7;
270
271        if (getBeginDateTime().getMinuteOfDay() == 0) {
272            // "Standard" days.
273            for (int i = firstDay; i < lastDay; i++) {
274                DateTime currDay = getBeginDateTime().plusDays(i);
275                dayStrings.add(currDay.toString("E"));
276            }
277        } else {
278            // Day Split Strings
279
280            for (int i = firstDay; i < lastDay; i++) {
281                StringBuilder builder = new StringBuilder("");
282                DateTime currStart = getBeginDateTime().plusDays(i);
283                DateTime currEnd = getBeginDateTime().plusDays(i);
284
285                builder.append(currStart.toString("E HH:mm"));
286                builder.append(" - ");
287                builder.append(currEnd.toString("E HH:mm"));
288
289                dayStrings.add(builder.toString());
290            }
291        }
292
293        return dayStrings;
294    }
295
296    public String getCalenadrYear() {
297        return getBeginDateTime().toString("yyyy");
298    }
299
300    public String getCalendarMonth() {
301        return getBeginDateTime().toString("M");
302    }
303
304    private String getDayNumberString(int dayDelta) {
305        StringBuilder b = new StringBuilder();
306
307        if (getBeginDateTime().getMinuteOfDay() == 0) {
308            DateTime currStart = getBeginDateTime().plusDays(dayDelta);
309            b.append(currStart.toString("d"));
310        } else {
311            DateTime currStart = getBeginDateTime().plusDays(dayDelta);
312            DateTime currEnd = getBeginDateTime().plusDays(dayDelta);
313
314            b.append(currStart.toString("d HH:mm"));
315            b.append(" - ");
316            //TODO: should this be currEnd???
317            b.append(currStart.toString("d HH:mm"));
318        }
319
320        return b.toString();
321    }
322
323    private String getDateString(int dayDelta) {
324        return getBeginDateTime().plusDays(dayDelta).toString("M/d/yyyy");
325    }
326
327}