/*
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.xmpp.workgroup;

import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;

public class Schedule {

    private long id;
    private boolean[] week = new boolean[7];
    private final String[] dayNames = new DateFormatSymbols().getShortWeekdays();
    private final List<String> dayNamesList = Arrays.asList(dayNames);
    private TreeSet<Schedule.Event> events = new TreeSet<Schedule.Event>();
    private final String[] weekdays = new DateFormatSymbols().getShortWeekdays();

    public Schedule(long id) {
        this.id = id;
        Arrays.fill(week, false);
    }

    public Schedule(long id, String settings) {
        this(id);
        parse(settings);
    }

    public void clear() {
        events = new TreeSet<Schedule.Event>();
        Arrays.fill(week, false);
    }

    public long getID() {
        return id;
    }

    public boolean[] getWeek() {
        return week;
    }

    public SortedSet<Schedule.Event> getEvents() {
        return events;
    }

    public void parse(String data) {
        events.clear();
        Arrays.fill(week, false);
        if (data != null) {
            data = data.trim();
            int dayIndex = data.indexOf('#');
            String days;
            String events = null;
            if (dayIndex == -1) {
                // Just days
                days = data;
            }
            else {
                days = data.substring(0, dayIndex);
                data = data.substring(dayIndex + 1).trim();
                int eventIndex = data.indexOf('#');
                if (eventIndex == -1) {
                    // Just events
                    events = data;
                }
                else {
                    events = data.substring(0, eventIndex).trim();
                }
            }
            if (days.length() > 0) {
                StringTokenizer tokenizer = new StringTokenizer(days, ",", false);
                while (tokenizer.hasMoreTokens()) {
                    String day = tokenizer.nextToken().trim();
                    for (int i = 0; i < dayNames.length; i++) {
                        if (dayNames[i].equalsIgnoreCase(day)) {
                            week[i] = true;
                        }
                    }
                }
            }
            if (events != null && events.length() > 0) {
                StringTokenizer tokenizer = new StringTokenizer(events, ",", false);
                while (tokenizer.hasMoreTokens()) {
                    String event = tokenizer.nextToken();
                    StringTokenizer eventTokenizer = new StringTokenizer(event, "-", false);
                    int hour = Integer.parseInt(eventTokenizer.nextToken());
                    int min = Integer.parseInt(eventTokenizer.nextToken());
                    boolean on = "1".equals(eventTokenizer.nextToken());
                    this.events.add(new Event(hour, min, on));
                }
            }
        }
    }

    @Override
	public String toString() {
        StringBuilder schedule = new StringBuilder();
        boolean needsComma = false;
        for (int i = 0; i < week.length; i++) {
            if (week[i]) {
                if (needsComma) {
                    schedule.append(',');
                }
                schedule.append(dayNames[i]);
                needsComma = true;
            }
        }
        schedule.append('#');
        needsComma = false;
        for(Schedule.Event event : events) {
            if (needsComma) {
                schedule.append(',');
            }
            schedule.append(event.toString());
            needsComma = true;
        }
        return schedule.toString();
    }

    public boolean isOpened(Calendar now) {
        int index = 0;
        boolean isOpened;
        try {
            index = dayNamesList.indexOf(weekdays[now.get(Calendar.DAY_OF_WEEK)]);
            isOpened = getWeek()[index];
        }
        catch (Exception e) {
            return false;
        }

        return isOpened;
    }

    /**
        * <p>An event that will occur in the schedule.</p>
        * <p>Event times are:</p>
        * <ul>
        * <li>hour - any number between 0 and 23 with 0 being midnight.</li>
        * <li>minute - any minute between 0 and 59.</li>
        * </ul>
        */
      public static class Event implements Comparable<Event>{
           private int hour;
           private int minute;
           private boolean on;
           /**
            * <p>Create an event.</p>
            *
            * @param hour The hour this event occurs
            * @param minute The minute this event occurs
            * @param on True if the event turns on the associated resource
            */
           public Event(int hour, int minute, boolean on){
               setHour(hour);
               setMinute(minute);
               setOn(on);
           }

           /**
            * <p>Obtain the hour for this event.</p>
            *
            * @return The hour of this event
            */
           public int getHour() {
               return hour;
           }

           /**
            * <p>Set the hour for this event.</p>
            *
            * @param hour The hour of this event
            */
           public void setHour(int hour) {
               this.hour = hour;
           }

           /**
            * <p>Obtain the minute for this event.</p>
            *
            * @return The minute of this event
            */
           public int getMinute() {
               return minute;
           }

           /**
            * @param minute
            */
           public void setMinute(int minute) {
               this.minute = minute;
           }

           /**
            * <p>Get the flag indicating this event is to trigger the resource to turn on or off.</p>
            *
            * @return True if this event turns the resource on
            */
           public boolean isOn() {
               return on;
           }

           /**
            * <p>Set a flag indicating this event is to trigger the resource to turn on or off.</p>
            *
            * @param on True if this event turns the resource on
            */
           public void setOn(boolean on) {
               this.on = on;
           }

           /**
            * <p>Compares events based on time.</p>
            *
            * <p>Comparisons against non Event objects will result in a class cast exception.</p>
            *
            * @param o The object to compare with this one
            * @return negative, zero, or positive int if the object is less than, equal to, or greater than the specified object
            */
           public int compareTo(Event event) {
               int val = hour - event.hour;
               if (val == 0){
                   val = minute - event.minute;
               }
               return val;
           }

           /**
            * <p>Generate the event string in proper format for parsing.</p>
            *
            * @return The event as a standard schedule string
            */
           @Override
		public String toString(){
               StringBuilder event = new StringBuilder(Integer.toString(hour));
               event.append('-');
               event.append(Integer.toString(minute));
               event.append('-');
               event.append(on ? '1' : '0');
               return event.toString();
           }

           @Override
		public boolean equals(Object o){
               boolean eq = false;
               if (o instanceof Event){
                   Event event = (Event)o;
                   eq = event.hour == hour && event.minute == minute && event.on == on;
               }
               return eq;
           }
       }
   }

