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

import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Currency;
import java.util.Locale;
import java.util.Optional;

import org.supercsv.cellprocessor.ift.CellProcessor;

import com.github.mygreen.supercsv.annotation.constraint.CsvNumberMax;
import com.github.mygreen.supercsv.annotation.constraint.CsvNumberMin;
import com.github.mygreen.supercsv.annotation.constraint.CsvNumberRange;
import com.github.mygreen.supercsv.annotation.format.CsvNumberFormat;
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.NumberMaxFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.NumberMinFactory;
import com.github.mygreen.supercsv.cellprocessor.constraint.NumberRangeFactory;
import com.github.mygreen.supercsv.cellprocessor.format.NumberFormatWrapper;
import com.github.mygreen.supercsv.cellprocessor.format.SimpleNumberFormatter;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.util.Utils;


/**
 * 数値型に対する{@link CellProcessor}を組み立てるクラス。
 * <p>各種タイプごとに実装を行う。</p>
 * 
 * @version 2.0
 * @author T.TSUCHIE
 *
 */
public abstract class AbstractNumberProcessorBuilder<N extends Number & Comparable<N>> extends AbstractProcessorBuilder<N> {
    
    public AbstractNumberProcessorBuilder() {
        super();
        
    }
    
    @Override
    protected void init() {
        super.init();
        
        // 制約のアノテーションの追加
        registerForConstraint(CsvNumberRange.class, new NumberRangeFactory<>());
        registerForConstraint(CsvNumberMin.class, new NumberMinFactory<>());
        registerForConstraint(CsvNumberMax.class, new NumberMaxFactory<>());
        
        
    }
    
    @Override
    protected TextFormatter<N> getDefaultFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<NumberFormatWrapper<N>> formatter = createFormatter(field, config);
        if(formatter.isPresent()) {
            return formatter.get();
            
        } else {
            return createSimpleFormatter(field, config);
            
        }
        
    }
    
    /**
     * 数値のフォーマッタを作成する。
     * <p>アノテーション{@link CsvNumberFormat}の値を元に作成します。</p>
     * @param field フィールド情報
     * @param config システム設定
     * @return アノテーション{@link CsvNumberFormat}が付与されていない場合は、空を返す。
     */
    @SuppressWarnings("unchecked")
    protected Optional<NumberFormatWrapper<N>> createFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvNumberFormat> formatAnno = field.getAnnotation(CsvNumberFormat.class);
        
        if(!formatAnno.isPresent()) {
            return Optional.empty();
        }
        
        final String pattern = formatAnno.get().pattern();
        if(pattern.isEmpty()) {
            return Optional.empty();
        }
        
        final boolean lenient = formatAnno.get().lenient();
        final Locale locale = Utils.getLocale(formatAnno.get().locale());
        final Optional<Currency> currency = formatAnno.get().currency().isEmpty() ? Optional.empty()
                : Optional.of(Currency.getInstance(formatAnno.get().currency()));
        final RoundingMode roundingMode = formatAnno.get().rounding();
        final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
        
        final DecimalFormat formatter = new DecimalFormat(pattern, symbols);
        formatter.setParseBigDecimal(true);
        formatter.setRoundingMode(roundingMode);
        currency.ifPresent(c -> formatter.setCurrency(c));
        
        final NumberFormatWrapper<N> wrapper = new NumberFormatWrapper<>(formatter, (Class<N>)field.getType(), lenient);
        wrapper.setValidationMessage(formatAnno.get().message());
        
        return Optional.of(wrapper);
        
    }
    
    /**
     * アノテーション{@link CsvNumberFormat}の指定がないときなどの書式のない数値のフォーマッタの作成。
     * <p>{@link #createFormatter(FieldAccessor, Configuration)}で結果が空の時に使用する。</p>
     * 
     * @param field フィールド情報
     * @param config システム設定
     * @return
     */
    @SuppressWarnings("unchecked")
    protected SimpleNumberFormatter<N> createSimpleFormatter(final FieldAccessor field, final Configuration config) {
        
        final Optional<CsvNumberFormat> formatAnno = field.getAnnotation(CsvNumberFormat.class);
        if(!formatAnno.isPresent()) {
            return new SimpleNumberFormatter<>((Class<N>)field.getType(), false);
        }
        
        final boolean lenient = formatAnno.get().lenient();
        final RoundingMode roundingMode = formatAnno.get().rounding();
        final int precision = formatAnno.get().precision();
        
        final SimpleNumberFormatter<N> formatter;
        if(precision >= 0) {
            formatter = new SimpleNumberFormatter<>((Class<N>)field.getType(), lenient, new MathContext(precision, roundingMode));
            
        } else {
            formatter = new SimpleNumberFormatter<>((Class<N>)field.getType(), lenient);
            
        }
        
        formatter.setValidationMessage(formatAnno.get().message());
        
        return formatter;
        
    }
    
}