package ru.yandex.wmtools.common.data.wrappers;

import ru.yandex.common.util.collections.Pair;
import ru.yandex.wmtools.common.util.XmlDataWrapper;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author avhaliullin
 */
public class PairWrapper<A, AW extends XmlDataWrapper<A>, B, BW extends XmlDataWrapper<B>> extends XmlDataWrapper<Pair<A, B>> {
    private final Class<AW> firstWrapper;
    private final Class<BW> secondWrapper;

    public PairWrapper(Pair<A, B> data, String name, Class<AW> firstWrapper, Class<BW> secondWrapper) {
        super(data, name);
        this.firstWrapper = firstWrapper;
        this.secondWrapper = secondWrapper;
    }

    private <T, W extends XmlDataWrapper<T>> W getWrapper(T data, Class<W> wrapperClass) {
        try {
            Constructor<?>[] constructors = wrapperClass.getConstructors();
            Constructor<W> correctConstructor = null;
            for (Constructor<?> constructor : constructors) {
                if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0].isAssignableFrom(data.getClass())) {
                    correctConstructor = wrapperClass.getConstructor(constructor.getParameterTypes()[0]);
                    break;
                }
            }
            if (correctConstructor == null) {
                throw new IllegalArgumentException("Cannot find constructor!");
            }
            return correctConstructor.newInstance(data);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Cannot find constructor!", e);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Wrong arguments for constructor!", e);
        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException(e);
        } catch (InstantiationException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    protected void doToXml(StringBuilder result) {
        getWrapper(data.getFirst(), firstWrapper).toXml(result);
        getWrapper(data.getSecond(), secondWrapper).toXml(result);
    }
}
