package andreluiznsilva.architecture.java.jee7.infra.architecture.localization;

import java.math.RoundingMode;
import java.text.*;
import java.util.*;

import andreluiznsilva.architecture.java.jee7.infra.util.ValidationUtils;

public class DefaultLocalizationProvider implements LocalizationProvider {

	private static final String BUNDLE_NAME = "localization";

	@Override
	public String formatCurrency(final Number value) {
		return getCurrencyFormat().format(value);
	}

	@Override
	public String formatDate(final Date date, final FormatStyle dateStyle) {
		return getDateFormat(dateStyle).format(date);
	}

	@Override
	public String formatDateIso(final Date date) {
		return getIsoDateFormat().format(date);
	}

	@Override
	public String formatDateTime(final Date date, final FormatStyle dateStyle, final FormatStyle timeStyle) {
		return getDateTimeFormat(dateStyle, timeStyle).format(date);
	}

	@Override
	public String formatNumber(final Number value) {
		return formatNumber(value, DIGITS_DEFAULT);
	}

	@Override
	public String formatNumber(final Number value, final Integer digits) {
		return getNumberFormat(digits).format(value);
	}

	@Override
	public String formatTime(final Date date, final FormatStyle timeStyle) {
		return getTimeFormat(timeStyle).format(date);
	}

	@Override
	public Locale getLocale() {
		return Locale.getDefault();
	}

	@Override
	public String getMessage(final String key, final Object... parameters) {

		final Locale locale = getLocale();

		final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);

		String message = null;

		try {

			message = bundle.getString(key);

			if (ValidationUtils.isNotBlank(message)) {
				message = new MessageFormat(message, locale).format(parameters);
			}

		} catch (final MissingResourceException e) {
			message = key;
		}

		return message;

	}

	@Override
	public TimeZone getTimeZone() {
		return TimeZone.getDefault();
	}

	@Override
	public Number parseCurrency(final String number) {
		try {
			return getCurrencyFormat().parse(number);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Date parseDate(final String date, final FormatStyle dateStyle) {
		try {
			return getDateFormat(dateStyle).parse(date);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Date parseDateIso(final String date) {
		try {
			return getIsoDateFormat().parse(date);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Date parseDateTime(final String date, final FormatStyle dateStyle, final FormatStyle timeStyle) {
		try {
			return getDateTimeFormat(dateStyle, timeStyle).parse(date);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Number parseNumber(final String number) {
		try {
			return getNumberFormat(DIGITS_DEFAULT).parse(number);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Number parseNumber(final String number, final Integer digits) {
		try {
			final NumberFormat format = getNumberFormat(digits);
			return format.parse(formatNumber(format.parse(number), digits));
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Date parseTime(final String date, final FormatStyle timeStyle) {
		try {
			return getTimeFormat(timeStyle).parse(date);
		} catch (final ParseException e) {
			throw new RuntimeException(e);
		}
	}

	private NumberFormat getCurrencyFormat() {
		return NumberFormat.getCurrencyInstance(getLocale());
	}

	private DateFormat getDateFormat(final FormatStyle dateStyle) {
		ValidationUtils.checkArgumentNotNull(dateStyle, "DateStyle shoud be not null");
		final DateFormat format = DateFormat.getDateInstance(dateStyle.getFormat(), getLocale());
		format.setTimeZone(getTimeZone());
		return format;
	}

	private DateFormat getDateTimeFormat(final FormatStyle dateStyle, final FormatStyle timeStyle) {
		ValidationUtils.checkArgumentNotNull(dateStyle, "DateStyle shoud be not null");
		ValidationUtils.checkArgumentNotNull(timeStyle, "TimeStyle shoud be not null");
		final DateFormat format = DateFormat.getDateTimeInstance(dateStyle.getFormat(), timeStyle.getFormat(), getLocale());
		format.setTimeZone(getTimeZone());
		return format;
	}

	private DateFormat getIsoDateFormat() {
		final DateFormat format = new SimpleDateFormat(PATTERN_ISO_DATE, getLocale());
		format.setTimeZone(TIMEZONE_UTC);
		return format;
	}

	private NumberFormat getNumberFormat(final Integer digits) {
		ValidationUtils.checkArgumentNotNull(digits, "Digits shoud be not null");
		final NumberFormat formater = NumberFormat.getInstance(getLocale());
		formater.setMinimumFractionDigits(digits);
		formater.setMaximumFractionDigits(digits);
		formater.setRoundingMode(RoundingMode.HALF_UP);
		return formater;
	}

	private DateFormat getTimeFormat(final FormatStyle timeStyle) {
		ValidationUtils.checkArgumentNotNull(timeStyle, "TimeStyle shoud be not null");
		final DateFormat format = DateFormat.getTimeInstance(timeStyle.getFormat(), getLocale());
		format.setTimeZone(getTimeZone());
		return format;
	}

}