package core.util;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.logging.log4j.LogManager;

import core.config.Factory;

/**
 * 数値ユーティリティ
 * @author Tadashi Nakayama
 */
public final class NumberUtil {

	/** 数字判断パターン */
	private static final Pattern PATTERN_NUMBER = Pattern.compile("^[\\-\\+0-9 ,\\.]+$");

	/**
	 * コンストラクタ
	 */
	private NumberUtil() {
		throw new AssertionError();
	}

	/**
	 * セパレータ除去
	 * @param val 文字列
	 * @return 除去後文字列
	 */
	public static String removeSeparator(final String val) {
		return val != null ? val.replaceAll("[, ]", "") : null;
	}

	/**
	 * 加算
	 * @param vals Integer配列
	 * @return 合計
	 */
	public static Integer add(final Integer... vals) {
		return Optional.ofNullable(vals).flatMap(
				v -> Stream.of(v).filter(Objects::nonNull).reduce(Integer::sum)
		).orElse(null);
	}

	/**
	 * 加算
	 * @param vals Long配列
	 * @return 合計
	 */
	public static Long add(final Long... vals) {
		return Optional.ofNullable(vals).flatMap(
				v -> Stream.of(v).filter(Objects::nonNull).reduce(Long::sum)
		).orElse(null);
	}

	/**
	 * 加算
	 * @param vals BigDecimal配列
	 * @return 合計
	 */
	public static BigDecimal add(final BigDecimal... vals) {
		return Optional.ofNullable(vals).flatMap(
				v -> Stream.of(v).filter(Objects::nonNull).reduce(BigDecimal::add)
		).orElse(null);
	}

	/**
	 * 乗算
	 * @param vals BigDecimal配列
	 * @return 乗算結果
	 */
	public static BigDecimal multiply(final BigDecimal... vals) {
		return Optional.ofNullable(vals).flatMap(
			v -> Stream.of(v).filter(Objects::nonNull).reduce(BigDecimal::multiply)
		).orElse(null);
	}

	/**
	 * 減算
	 * @param base 引き元数
	 * @param vals BigDecimal配列
	 * @return 減算結果
	 */
	public static BigDecimal subtract(final BigDecimal base, final BigDecimal... vals) {
		return Optional.ofNullable(base).map(
			b -> {
				Optional.ofNullable(vals).ifPresent(
					v -> Stream.of(v).filter(Objects::nonNull).forEach(b::subtract)
				);
				return b;
			}
		).orElse(null);
	}

	/**
	 * Long化
	 * @param val 文字列
	 * @return Long
	 */
	public static Long toLong(final String val) {
		return toLong(val, null);
	}

	/**
	 * Long化
	 * @param val 文字列
	 * @param def デフォルト値
	 * @return Long
	 */
	public static Long toLong(final String val, final Long def) {
		if (!Objects.toString(val, "").isEmpty()) {
			try {
				return Long.valueOf(removeSeparator(val));
			} catch (final NumberFormatException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
		}
		return def;
	}

	/**
	 * Long値化
	 * @param num Number
	 * @return Long値
	 */
	public static Long toLong(final Number num) {
		return toLong(num, null);
	}

	/**
	 * Long化
	 * @param num Number
	 * @param def デフォルト値
	 * @return Long
	 */
	public static Long toLong(final Number num, final Long def) {
		if (Long.class.isInstance(num)) {
			return Long.class.cast(num);
		}
		return num != null ? Long.valueOf(num.longValue()) : def;
	}

	/**
	 * int化
	 * @param val 文字列
	 * @param def デフォルト値
	 * @return int
	 */
	public static int toInt(final String val, final int def) {
		return toInteger(val, Integer.valueOf(def)).intValue();
	}

	/**
	 * Integer化
	 * @param val 文字列
	 * @return Integer
	 */
	public static Integer toInteger(final String val) {
		return toInteger(val, null);
	}

	/**
	 * Integer化
	 * @param val 文字列
	 * @param def デフォルト値
	 * @return Integer
	 */
	public static Integer toInteger(final String val, final Integer def) {
		if (!Objects.toString(val, "").isEmpty()) {
			try {
				return Integer.valueOf(removeSeparator(val));
			} catch (final NumberFormatException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
		}
		return def;
	}

	/**
	 * Integer値化
	 * @param num Number
	 * @return Integer値
	 */
	public static Integer toInteger(final Number num) {
		return toInteger(num, null);
	}

	/**
	 * Integer値化
	 * @param num Number
	 * @param def デフォルト値
	 * @return Integer値
	 */
	public static Integer toInteger(final Number num, final Integer def) {
		if (Integer.class.isInstance(num)) {
			return Integer.class.cast(num);
		}
		return num != null ? Integer.valueOf(num.intValue()) : def;
	}

	/**
	 * BigDecimal化（全角数字等は変換しない。）
	 * @param val 文字列
	 * @return BigDecimal
	 */
	public static BigDecimal toBigDecimal(final String val) {
		return toBigDecimal(val, null);
	}

	/**
	 * BigDecimal化（全角数字等は変換しない。）
	 * @param val 文字列
	 * @param def デフォルト値
	 * @return BigDecimal
	 */
	public static BigDecimal toBigDecimal(final String val, final BigDecimal def) {
		if (val != null && PATTERN_NUMBER.matcher(val).matches()) {
			try {
				return new BigDecimal(removeSeparator(val));
			} catch (final NumberFormatException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
		}
		return def;
	}

	/**
	 * BigDecimal値化
	 * @param num Number
	 * @return Integer値
	 */
	public static BigDecimal toBigDecimal(final Number num) {
		return toBigDecimal(num, null);
	}

	/**
	 * BigDecimal値化
	 * @param num Number
	 * @param def デフォルト値
	 * @return Integer値
	 */
	public static BigDecimal toBigDecimal(final Number num, final BigDecimal def) {
		if (BigDecimal.class.isInstance(num)) {
			return BigDecimal.class.cast(num);
		}
		return num != null ? new BigDecimal(num.toString()) : def;
	}

	/**
	 * Numberオブジェクト化
	 * @param <T> Numberジェネリックス
	 * @param val 文字列
	 * @param cls Numberクラス
	 * @return Numberオブジェクト
	 */
	public static <T extends Number> T toNumber(final String val, final Class<T> cls) {
		if (val != null) {
			try {
				return Factory.construct(
						Factory.getConstructor(cls, String.class), removeSeparator(val));
			} catch (final NumberFormatException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
		}
		return null;
	}
}
