/*
 *******************************************************************************
 * Copyright (C) 2007-2010, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.util;

import java.io.Serializable;

/**
 * <code>DateTimeRule</code> is a class representing a time in a year by
 * a rule specified by month, day of month, day of week and
 * time in the day.
 * 
 * @stable ICU 3.8
 */
public class DateTimeRule implements Serializable {

    private static final long serialVersionUID = 2183055795738051443L;

    /**
     * Date rule type defined by exact day of month.
     * For example, March 14.
     * 
     * @stable ICU 3.8
     */
    public static final int DOM = 0;

    /**
     * Date rule type defined by day of week in month.
     * For example, 2nd Sunday in March.
     * 
     * @stable ICU 3.8
     */
    public static final int DOW = 1;

    /**
     * Date rule type defined by first day of week on or
     * after exact day of month.
     * For example, 1st Monday on or after March 15.
     * 
     * @stable ICU 3.8
     */
    public static final int DOW_GEQ_DOM = 2;

    /**
     * Date rule type defined by last day of week on or
     * before exact day of month.
     * For example, last Saturday on or before March 15.
     * 
     * @stable ICU 3.8
     */
    public static final int DOW_LEQ_DOM = 3;
    
    /**
     * Time rule type for local wall time.
     * 
     * @stable ICU 3.8
     */
    public static final int WALL_TIME = 0;

    /**
     * Time rule type for local standard time.
     * 
     * @stable ICU 3.8
     */
    public static final int STANDARD_TIME = 1;

    /**
     * Time rule type for coordinated universal time.
     * 
     * @stable ICU 3.8
     */
    public static final int UTC_TIME = 2;

    // private stuff
    private final int dateRuleType;
    private final int month;
    private final int dayOfMonth;
    private final int dayOfWeek;
    private final int weekInMonth;

    private final int timeRuleType;
    private final int millisInDay;

    /**
     * Constructs a <code>DateTimeRule</code> by the day of month and
     * the time rule.  The date rule type for an instance created by
     * this constructor is <code>DOM</code>.
     * 
     * @param month         The rule month, for example, <code>Calendar.JANUARY</code>
     * @param dayOfMonth    The day of month, 1-based.
     * @param millisInDay   The milliseconds in the rule date.
     * @param timeType      The time type, <code>WALL_TIME</code> or <code>STANDARD_TIME</code>
     *                      or <code>UTC_TIME</code>.
     * 
     * @stable ICU 3.8
     */
    public DateTimeRule(int month, int dayOfMonth,
            int millisInDay, int timeType) {
        dateRuleType = DOM;
        this.month = month;
        this.dayOfMonth = dayOfMonth;

        this.millisInDay = millisInDay;
        this.timeRuleType = timeType;
        
        // not used by this rule type
        this.dayOfWeek = 0;
        this.weekInMonth = 0;
    }

    /**
     * Constructs a <code>DateTimeRule</code> by the day of week and its oridinal
     * number and the time rule.  The date rule type for an instance created
     * by this constructor is <code>DOW</code>.
     * 
     * @param month         The rule month, for example, <code>Calendar.JANUARY</code>.
     * @param weekInMonth   The ordinal number of the day of week.  Negative number
     *                      may be used for specifying a rule date counted from the
     *                      end of the rule month.
     * @param dayOfWeek     The day of week, for example, <code>Calendar.SUNDAY</code>.
     * @param millisInDay   The milliseconds in the rule date.
     * @param timeType      The time type, <code>WALL_TIME</code> or <code>STANDARD_TIME</code>
     *                      or <code>UTC_TIME</code>.
     * 
     * @stable ICU 3.8
     */
    public DateTimeRule(int month, int weekInMonth, int dayOfWeek,
            int millisInDay, int timeType) {
        dateRuleType = DOW;
        this.month = month;
        this.weekInMonth = weekInMonth;
        this.dayOfWeek = dayOfWeek;

        this.millisInDay = millisInDay;
        this.timeRuleType = timeType;

        // not used by this rule type
        this.dayOfMonth = 0;
    }

    /**
     * Constructs a <code>DateTimeRule</code> by the first/last day of week
     * on or after/before the day of month and the time rule.  The date rule
     * type for an instance created by this constructor is either
     * <code>DOM_GEQ_DOM</code> or <code>DOM_LEQ_DOM</code>.
     * 
     * @param month         The rule month, for example, <code>Calendar.JANUARY</code>
     * @param dayOfMonth    The day of month, 1-based.
     * @param dayOfWeek     The day of week, for example, <code>Calendar.SUNDAY</code>.
     * @param after         true if the rule date is on or after the day of month.
     * @param millisInDay   The milliseconds in the rule date.
     * @param timeType      The time type, <code>WALL_TIME</code> or <code>STANDARD_TIME</code>
     *                      or <code>UTC_TIME</code>.
     * 
     * @stable ICU 3.8
     */
    public DateTimeRule(int month, int dayOfMonth, int dayOfWeek, boolean after,
            int millisInDay, int timeType) {
        this.dateRuleType = after ? DOW_GEQ_DOM : DOW_LEQ_DOM;
        this.month = month;
        this.dayOfMonth = dayOfMonth;
        this.dayOfWeek = dayOfWeek;

        this.millisInDay = millisInDay;
        this.timeRuleType = timeType;

        // not used by this rule type
        this.weekInMonth = 0;
    }

    /**
     * Gets the date rule type, such as <code>DOM</code>
     * 
     * @return The date rule type.
     * 
     * @stable ICU 3.8
     */
    public int getDateRuleType() {
        return dateRuleType;
    }

    /**
     * Gets the rule month.
     * 
     * @return The rule month.
     * 
     * @stable ICU 3.8
     */
    public int getRuleMonth() {
        return month;
    }

    /**
     * Gets the rule day of month.  When the date rule type
     * is <code>DOW</code>, the value is always 0.
     * 
     * @return The rule day of month
     * 
     * @stable ICU 3.8
     */
    public int getRuleDayOfMonth() {
        return dayOfMonth;
    }

    /**
     * Gets the rule day of week.  When the date rule type
     * is <code>DOM</code>, the value is always 0.
     * 
     * @return The rule day of week.
     * 
     * @stable ICU 3.8
     */
    public int getRuleDayOfWeek() {
        return dayOfWeek;
    }

    /**
     * Gets the rule day of week ordinal number in the month.
     * When the date rule type is not <code>DOW</code>, the value is
     * always 0.
     * 
     * @return The rule day of week ordinal number in the month.
     * 
     * @stable ICU 3.8
     */
    public int getRuleWeekInMonth() {
        return weekInMonth;
    }

    /**
     * Gets the time rule type
     * 
     * @return The time rule type, either <code>WALL_TIME</code> or <code>STANDARD_TIME</code>
     *         or <code>UTC_TIME</code>.
     * 
     * @stable ICU 3.8
     */
    public int getTimeRuleType() {
        return timeRuleType;
    }

    /**
     * Gets the rule time in the rule day.
     * 
     * @return The time in the rule day in milliseconds.
     * 
     * @stable ICU 3.8
     */
    public int getRuleMillisInDay() {
        return millisInDay;
    }
    
    private static final String[] DOWSTR = {"", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    private static final String[] MONSTR = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

    /**
     * Returns a <code>String</code> representation of this <code>DateTimeRule</code> object.
     * This method is used for debugging purpose only.  The string representation can be changed
     * in future version of ICU without any notice.
     * 
     * @stable ICU 3.8
     */
    public String toString() {
        String sDate = null;
        String sTimeRuleType = null;

        switch (dateRuleType) {
        case DOM:
            sDate = Integer.toString(dayOfMonth);
            break;
        case DOW:
            sDate = Integer.toString(weekInMonth) + DOWSTR[dayOfWeek];
            break;
        case DOW_GEQ_DOM:
            sDate = DOWSTR[dayOfWeek] + ">=" + Integer.toString(dayOfMonth);
            break;
        case DOW_LEQ_DOM:
            sDate = DOWSTR[dayOfWeek] + "<=" + Integer.toString(dayOfMonth);
            break;
        }

        switch (timeRuleType) {
        case WALL_TIME:
            sTimeRuleType = "WALL";
            break;
        case STANDARD_TIME:
            sTimeRuleType = "STD";
            break;
        case UTC_TIME:
            sTimeRuleType = "UTC";
            break;
        }

        int time = millisInDay;
        int millis = time % 1000;
        time /= 1000;
        int secs = time % 60;
        time /= 60;
        int mins = time % 60;
        int hours = time / 60;

        StringBuilder buf = new StringBuilder();
        buf.append("month=");
        buf.append(MONSTR[month]);
        buf.append(", date=");
        buf.append(sDate);
        buf.append(", time=");
        buf.append(hours);
        buf.append(":");
        buf.append(mins/10);
        buf.append(mins%10);
        buf.append(":");
        buf.append(secs/10);
        buf.append(secs%10);
        buf.append(".");
        buf.append(millis/100);
        buf.append((millis/10)%10);
        buf.append(millis%10);
        buf.append("(");
        buf.append(sTimeRuleType);
        buf.append(")");
        return buf.toString();
    }
}
