package com.android.contacts.interactions;

import com.android.contacts.R;

import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;

import java.util.Formatter;
import java.util.Locale;

/**
 * The following methods were pulled from
 * {@link com.android.calendar.EventInfoFragment.updateEvent(View view)}
 * TODO: Move this to frameworks/opt
 */
public class CalendarInteractionUtils {

    // Using int constants as a return value instead of an enum to minimize resources.
    private static final int TODAY = 1;
    private static final int TOMORROW = 2;
    private static final int NONE = 0;

    /**
     * Returns a string description of the specified time interval.
     */
    public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis,
            String localTimezone, boolean allDay, Context context) {
        // Configure date/time formatting.
        int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
        int flagsTime = DateUtils.FORMAT_SHOW_TIME;
        if (DateFormat.is24HourFormat(context)) {
            flagsTime |= DateUtils.FORMAT_24HOUR;
        }

        Time currentTime = new Time(localTimezone);
        currentTime.set(currentMillis);
        Resources resources = context.getResources();
        String datetimeString = null;
        if (allDay) {
            // All day events require special timezone adjustment.
            long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone);
            long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone);
            if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) {
                // If possible, use "Today" or "Tomorrow" instead of a full date string.
                int todayOrTomorrow = isTodayOrTomorrow(context.getResources(),
                        localStartMillis, currentMillis, currentTime.gmtoff);
                if (TODAY == todayOrTomorrow) {
                    datetimeString = resources.getString(R.string.today);
                } else if (TOMORROW == todayOrTomorrow) {
                    datetimeString = resources.getString(R.string.tomorrow);
                }
            }
            if (datetimeString == null) {
                // For multi-day allday events or single-day all-day events that are not
                // today or tomorrow, use framework formatter.
                Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
                datetimeString = DateUtils.formatDateRange(context, f, startMillis,
                        endMillis, flagsDate, Time.TIMEZONE_UTC).toString();
            }
        } else {
            if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) {
                // Format the time.
                String timeString = formatDateRange(context, startMillis, endMillis,
                        flagsTime);

                // If possible, use "Today" or "Tomorrow" instead of a full date string.
                int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis,
                        currentMillis, currentTime.gmtoff);
                if (TODAY == todayOrTomorrow) {
                    // Example: "Today at 1:00pm - 2:00 pm"
                    datetimeString = resources.getString(R.string.today_at_time_fmt,
                            timeString);
                } else if (TOMORROW == todayOrTomorrow) {
                    // Example: "Tomorrow at 1:00pm - 2:00 pm"
                    datetimeString = resources.getString(R.string.tomorrow_at_time_fmt,
                            timeString);
                } else {
                    // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm"
                    String dateString = formatDateRange(context, startMillis, endMillis,
                            flagsDate);
                    datetimeString = resources.getString(R.string.date_time_fmt, dateString,
                            timeString);
                }
            } else {
                // For multiday events, shorten day/month names.
                // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm"
                int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH |
                        DateUtils.FORMAT_ABBREV_WEEKDAY;
                datetimeString = formatDateRange(context, startMillis, endMillis,
                        flagsDatetime);
            }
        }
        return datetimeString;
    }

    /**
     * Convert given UTC time into current local time. This assumes it is for an
     * allday event and will adjust the time to be on a midnight boundary.
     *
     * @param recycle Time object to recycle, otherwise null.
     * @param utcTime Time to convert, in UTC.
     * @param tz The time zone to convert this time to.
     */
    private static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) {
        if (recycle == null) {
            recycle = new Time();
        }
        recycle.timezone = Time.TIMEZONE_UTC;
        recycle.set(utcTime);
        recycle.timezone = tz;
        return recycle.normalize(true);
    }

    public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) {
        if (recycle == null) {
            recycle = new Time();
        }
        recycle.timezone = tz;
        recycle.set(localTime);
        recycle.timezone = Time.TIMEZONE_UTC;
        return recycle.normalize(true);
    }

    /**
     * Returns whether the specified time interval is in a single day.
     */
    private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) {
        if (startMillis == endMillis) {
            return true;
        }

        // An event ending at midnight should still be a single-day event, so check
        // time end-1.
        int startDay = Time.getJulianDay(startMillis, localGmtOffset);
        int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset);
        return startDay == endDay;
    }

    /**
     * Returns TODAY or TOMORROW if applicable.  Otherwise returns NONE.
     */
    private static int isTodayOrTomorrow(Resources r, long dayMillis,
            long currentMillis, long localGmtOffset) {
        int startDay = Time.getJulianDay(dayMillis, localGmtOffset);
        int currentDay = Time.getJulianDay(currentMillis, localGmtOffset);

        int days = startDay - currentDay;
        if (days == 1) {
            return TOMORROW;
        } else if (days == 0) {
            return TODAY;
        } else {
            return NONE;
        }
    }

    /**
     * Formats a date or a time range according to the local conventions.
     *
     * This formats a date/time range using Calendar's time zone and the
     * local conventions for the region of the device.
     *
     * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in
     * the UTC time zone instead.
     *
     * @param context the context is required only if the time is shown
     * @param startMillis the start time in UTC milliseconds
     * @param endMillis the end time in UTC milliseconds
     * @param flags a bit mask of options See
     * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
     * @return a string containing the formatted date/time range.
     */
    private static String formatDateRange(Context context, long startMillis,
            long endMillis, int flags) {
        String date;
        String tz;
        if ((flags & DateUtils.FORMAT_UTC) != 0) {
            tz = Time.TIMEZONE_UTC;
        } else {
            tz = Time.getCurrentTimezone();
        }
        StringBuilder sb = new StringBuilder(50);
        Formatter f = new Formatter(sb, Locale.getDefault());
        sb.setLength(0);
        date = DateUtils.formatDateRange(context, f, startMillis, endMillis, flags,
                tz).toString();
        return date;
    }
}
