package jp.sourceforge.shovel.taglib;

import static jp.sourceforge.shovel.taglib.DateFormatTag.DateFormatType.*;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.Calendar;

import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.struts.taglib.TagUtils;

public class DateFormatTag extends TagSupport {
    static final long serialVersionUID = 1L;
    final String bundle_ = "message.date";
    
    enum DateFormatType {
        DATETIME_COMPACT_VARIABLE("dateTime.compact.variable"),
        DATETIME_COMPATIBLE_TWITTER("dateTime.compatible.twitter"),
        DATETIME_ISO8601("dateTime.ISO8601"),
        DATETIME_MIDDLE_YMDW_HM("dateTime.middle.YMDW_HM"),
        DATETIME_MIDDLE_MD_HM("dateTime.middle.MD_HM"),
        SPAN_LESS_D("span.less.D"),
        SPAN_LESS_H("span.less.H"),
        SPAN_LESS_M("span.less.M");
        
        String formatId_;
        static Map<String, DateFormatType> instances_;

        DateFormatType( String formatId ) {
            formatId_ = formatId;
        }
        public String toString() {
            return formatId_;
        }
        public boolean hasTime() {
            return formatId_.toLowerCase().indexOf("time") != -1;
        }
        public static DateFormatType getInstance( String formatId ) {
            if (instances_ == null) {
                instances_ = new HashMap<String, DateFormatType>();
                DateFormatType[] formats = values();
                for (DateFormatType format : formats) {
                    instances_.put(format.toString(), format);
                }
            }
            return instances_.get(formatId);
        }
    }

    TimeZone timeZone_ = TimeZone.getDefault();
    String lang_ = null;
    long date_;
    String format_ = "dateTime.compact.constant";

    public String getTimeZoneID() {
        return timeZone_.getID();
    }
    public void setTimeZoneID(String id) {
        timeZone_ = TimeZone.getTimeZone(id);
    }
    TimeZone getTimeZone() {
        return timeZone_;
    }
    public String getLang() {
        return lang_;
    }
    public void setLang(String lang) {
        lang_ = lang;
    }
    Locale getLocale() {
        if (lang_ == null) {
            //ここまできててSessionがないことはないはず。。。たぶん
            HttpSession session = pageContext.getSession();
            Locale locale = (Locale)session.getAttribute(org.apache.struts.Globals.LOCALE_KEY);
            lang_ = locale.getLanguage();
            return locale;
        }
        return new Locale(lang_);
    }
    public long getDate(){
        return date_;
    }
    public void setDate(long date) {
        date_ = date;
    }
    public String getFormat() {
        return format_;
    }
    public void setFormat(String format) {
        format_ = format;
    }
    /**
     * メッセージリソースの取得
     * 
     * @param key  メッセージのキー
     * @return メッセージ
     * @throws JspException
     */
    String getMessage(String key) throws JspException {
        TagUtils utils = TagUtils.getInstance();
        return utils.message(pageContext, bundle_, getLocale().toString(), key);
    }
    public int doStartTag() throws JspException {
        DateFormatType formatType = DateFormatType.getInstance(getFormat());
        Calendar calendar = Calendar.getInstance(getTimeZone(), getLocale());
        calendar.setTimeInMillis(getDate());
        
        formatType = getDateTimeCompactFormat(calendar, formatType);
        
        StringBuilder formatBuilder = new StringBuilder(formatType.toString());
        if (formatType.hasTime()) {
            //時間表記法はSessionを最優先
            String notation = (String)pageContext.getSession().getAttribute("time.notation");
            if (notation == null) {
                //ない場合はmessage.dateのTimeNotation
                notation = getMessage("time.notation");
            }
            formatBuilder.append(notation);
        }
        String pattern = getMessage(formatBuilder.toString());
        Calendar now = Calendar.getInstance(getTimeZone(), getLocale());
        long elapsedTime = now.getTimeInMillis() - calendar.getTimeInMillis();
        if (elapsedTime < 60 * 1000) {
            elapsedTime += 999;
            elapsedTime /= 1000;
        } else if (elapsedTime < 60 * 60 * 1000) {
            elapsedTime /= 60 * 1000;
        } else if (elapsedTime < 24 * 60 * 60 * 1000) {
            elapsedTime /= 60 * 60 * 1000;
        }
        
        Object[] arguments = new Object[]{calendar.getTime() , elapsedTime};
        MessageFormat formatter = new MessageFormat(pattern, getLocale());
        StringBuffer content = formatter.format(arguments, new StringBuffer(), null);
        try {
          pageContext.getOut().print(content.toString());
        } catch( Exception e ) {
            throw new JspException( e.getMessage() );
        }
        return SKIP_BODY;
    }
    public int doEndTag() {
        return EVAL_PAGE;
    }
    /**
     * 指定日時に合った書式を取得
     *
     * @param calendar  日時
     * @param format  タグから渡された書式
     *  DATETIME_COMPACT_VARIABLE  経過時間に合った書式を取得
     *    60分以内    TimeLess_H  分以内
     *    24時間以内  TimeLess_D  時間以内
     *    24時間以上  DateMiddle_YMDW_HM  yyyy/M/d(EEE) H:mm
     * @return  日時の書式
     */
    DateFormatType getDateTimeCompactFormat(Calendar calendar, DateFormatType formatType) {
        if (formatType == DATETIME_COMPACT_VARIABLE) {
            Calendar now = Calendar.getInstance(getTimeZone(), getLocale());
            long elapsedTime = now.getTimeInMillis() - calendar.getTimeInMillis();
            formatType = DATETIME_MIDDLE_MD_HM;
            if (elapsedTime < 60 * 1000) {
                formatType = SPAN_LESS_M;
            } else if (elapsedTime < 60 * 60 * 1000) {
                formatType = SPAN_LESS_H;
            } else if (elapsedTime < 24 * 60 * 60 * 1000) {
                formatType = SPAN_LESS_D;
            }
        }
        return formatType;
    }
}
