package ru.faulab.guice;

import com.google.inject.*;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryProvider;
import com.google.inject.internal.Nullable;
import com.google.inject.multibindings.MapBinder;

import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

/**
 * User: Dr.Faust
 * Date: 11.04.2009
 * Time: 13:38:05
 */
public class GuiceAssitedWithMultibindingHack
{
    public static interface PersistentDifferToLocaleStringFactory<Type>
    {
        PersistentDiffer<Type, String> create(Locale locale, GenericWrap<Type> initialValue);
    }

    public static interface DatePersistentDifferToLocaleStringFactory extends PersistentDifferToLocaleStringFactory<Date>
    {
        @Override
        PersistentDiffer<Date, String> create(Locale locale, GenericWrap<Date> initialValue);

    }

    public static interface IntegerPersistentDifferToLocaleStringFactory extends PersistentDifferToLocaleStringFactory<Integer>
    {
        @Override
        PersistentDiffer<Integer, String> create(Locale locale, GenericWrap<Integer> initialValue);
    }

    public static class GenericWrap<Value>
    {
        public final Value value;

        private GenericWrap(Value value)
        {
            this.value = value;
        }

        public static <Value> GenericWrap<Value> cgw(Value value)
        {
            return new GenericWrap<Value>(value);
        }
    }

    public static class PersistentDifferForDateToLocaleString implements PersistentDiffer<Date, String>
    {
        private final DateFormat dateForamt;
        private final Date initialValue;

        @Inject
        public PersistentDifferForDateToLocaleString(@Nullable @Assisted GenericWrap<Date> defaultValue, @Assisted Locale locale)
        {
            dateForamt = DateFormat.getDateInstance(DateFormat.FULL, locale);
            initialValue = defaultValue.value;
        }

        @Override
        public String getDiff(Date date)
        {
            if (initialValue == null || date == null)
            {
                return null;
            }
            Date targetDate = initialValue.before(date) ? date : initialValue;
            return dateForamt.format(targetDate);
        }
    }

    public static class PersistentDifferForIntegerToLocaleString implements PersistentDiffer<Integer, String>
    {
        private final NumberFormat numberFormat;
        private final Integer initialValue;

        @Inject
        public PersistentDifferForIntegerToLocaleString(@Nullable @Assisted GenericWrap<Integer> defaultValue, @Nullable @Assisted Locale locale)
        {
            numberFormat = NumberFormat.getNumberInstance(locale);
            initialValue = defaultValue.value;
        }

        @Override
        public String getDiff(Integer integer)
        {
            if (initialValue == null || integer == null)
            {
                return null;
            }
            return numberFormat.format(Math.max(integer, initialValue));
        }
    }

    public static class InStringPersistentDifferFactoryWithAssistedImpl implements InStringPersistentDifferFactory
    {
        private final Map<Class, PersistentDifferToLocaleStringFactory> converterMap;
        private final Locale locale;

        @Inject
        public InStringPersistentDifferFactoryWithAssistedImpl(Map<Class, PersistentDifferToLocaleStringFactory> converterMap)
        {
            this.converterMap = converterMap;
            locale = Locale.ROOT;
        }

        public <Type> PersistentDiffer<Type, String> createPersistentDiffer(Class<Type> type, Type value)
        {
            PersistentDifferToLocaleStringFactory<Type> persistentDiffer = converterMap.get(type);
            if (persistentDiffer == null)
            {
                return null;
            }
            return persistentDiffer.create(locale, GenericWrap.cgw(value));
        }
    }

    public static void main(String[] args)
    {
        Injector injector = Guice.createInjector(new PrivateModule()
        {
            @Override
            protected void configure()
            {
                MapBinder<Class, PersistentDifferToLocaleStringFactory> fromToStringConverterMultibinder = MapBinder.newMapBinder(binder(), Class.class, PersistentDifferToLocaleStringFactory.class);
                fromToStringConverterMultibinder.addBinding(Date.class).toProvider(FactoryProvider.newFactory(DatePersistentDifferToLocaleStringFactory.class, PersistentDifferForDateToLocaleString.class)).in(Singleton.class);
                fromToStringConverterMultibinder.addBinding(Integer.class).toProvider(FactoryProvider.newFactory(IntegerPersistentDifferToLocaleStringFactory.class, PersistentDifferForIntegerToLocaleString.class)).in(Singleton.class);
                bind(InStringPersistentDifferFactory.class).to(InStringPersistentDifferFactoryWithAssistedImpl.class).in(Singleton.class);
                expose(InStringPersistentDifferFactory.class);
            }
        });
        InStringPersistentDifferFactory persistentDifferFactory = injector.getInstance(InStringPersistentDifferFactory.class);

        PersistentDiffer<Date, String> datePersistentDiffer = persistentDifferFactory.createPersistentDiffer(Date.class, new Date());
        PersistentDiffer<Integer, String> integerPersistentDiffer = persistentDifferFactory.createPersistentDiffer(Integer.class, 10);

        System.out.println(datePersistentDiffer.getDiff(new Date()));
        System.out.println(datePersistentDiffer.getDiff(null));
        System.out.println(integerPersistentDiffer.getDiff(12));
        System.out.println(integerPersistentDiffer.getDiff(8));

    }
}