package com.github.mygreen.supercsv.builder.joda;

import java.util.Locale;
import java.util.Optional;
import java.util.TimeZone;

import org.joda.time.Chronology;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadablePartial;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.chrono.LenientChronology;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeRange;
import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeMin;
import com.github.mygreen.supercsv.annotation.constraint.CsvDateTimeMax;
import com.github.mygreen.supercsv.annotation.format.CsvDateTimeFormat;
import com.github.mygreen.supercsv.builder.AbstractProcessorBuilder;
import com.github.mygreen.supercsv.builder.Configuration;
import com.github.mygreen.supercsv.builder.FieldAccessor;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeRangeFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeMinFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.DateTimeMaxFactory;
import com.github.mygreen.supercsv.cellprocessor.format.JodaFormatWrapper;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.util.Utils;


/**
 * Joda-Time の{@link ReadablePartial}のテンプレートクラス。
 * <p>基本的に、{@link ReadablePartial}のサブクラスのビルダは、このクラスを継承して作成する。</p>
 *
 * @version 2.0
 * @since 1.2
 * @author T.TSUCHIE
 *
 */
public abstract class AbstractJodaProcessorBuilder<T> extends AbstractProcessorBuilder<T> {
    
    public AbstractJodaProcessorBuilder() {
        super();
        
    }
    
    @Override
    protected void init() {
        super.init();
        
        // 制約のアノテーションの追加
        registerForConstraint(CsvDateTimeRange.class, new DateTimeRangeFactory<>());
        registerForConstraint(CsvDateTimeMin.class, new DateTimeMinFactory<>());
        registerForConstraint(CsvDateTimeMax.class, new DateTimeMaxFactory<>());
        
    }
    
    /**
     * 変換規則から、{@link DateTimeFormatter}のインスタンスを作成する。
     * <p>アノテーション{@link CsvDateTimeFormat}が付与されていない場合は、各種タイプごとの標準の書式で作成する。</p>
     * @param field フィールド情報
     * @param config システム設定
     * @return {@link DateTimeFormatter}のインスタンス。
     */
    protected DateTimeFormatter createFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvDateTimeFormat> formatAnno = field.getAnnotation(CsvDateTimeFormat.class);
        if(!formatAnno.isPresent()) {
            return DateTimeFormat.forPattern(getDefaultPattern());
        }
        
        String pattern = formatAnno.get().pattern();
        if(pattern.isEmpty()) {
            pattern = getDefaultPattern();
        }
        
        final Locale locale = Utils.getLocale(formatAnno.get().locale());
        final DateTimeZone zone = formatAnno.get().timezone().isEmpty() ? DateTimeZone.getDefault()
                : DateTimeZone.forTimeZone(TimeZone.getTimeZone(formatAnno.get().timezone()));
        
        final DateTimeFormatter formatter = DateTimeFormat.forPattern(pattern)
                .withLocale(locale)
                .withZone(zone);
        
        final boolean lenient = formatAnno.get().lenient();
        if(lenient) {
            Chronology chronology = LenientChronology.getInstance(ISOChronology.getInstance());
            return formatter.withChronology(chronology);
            
        } else {
            return formatter;
        }
        
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected TextFormatter<T> getDefaultFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvDateTimeFormat> formatAnno = field.getAnnotation(CsvDateTimeFormat.class);
        final String pattern = getPattern(field);
        final DateTimeFormatter formatter = createFormatter(field, config);
        
        final JodaFormatWrapper<T> wrapper = new JodaFormatWrapper<>(formatter, (Class<T>)field.getType());
        wrapper.setPattern(pattern);
        formatAnno.ifPresent(a -> wrapper.setValidationMessage(a.message()));
        return wrapper;
    }
    
    /**
     * アノテーション{@link CsvDateTimeFormat}が付与されている場合は、パターンを取得する。
     * <p>ただし、アノテーションが付与されていない場合は、{@link #getDefaultPattern()}の値を返す。</p>
     * <p>{@link DateTimeFormatter}のインスタンスからは、元となったパターンは直接取得できないため、
     *    別途取得して利用するために使用する。</p>
     * @param field フィールド情報。
     * @return 日時の書式パターン。
     */
    protected String getPattern(final FieldAccessor field) {
        return field.getAnnotation(CsvDateTimeFormat.class)
                .map(a -> a.pattern())
                .filter(p -> !p.isEmpty())
                .orElse(getDefaultPattern());
    }
    
    /**
     * 変換規則用のアノテーションが定義されていないときの標準の書式を取得する。
     * 
     * @return {@link DateTimeFormatter}で解析可能な日時の書式。
     */
    protected abstract String getDefaultPattern();
    
}