package ru.yandex.autotests.direct.web.util.beanutils;

import java.util.function.Supplier;

import ch.lambdaj.function.convert.ArgumentConverter;
import ch.lambdaj.function.convert.Converter;

import ru.yandex.autotests.direct.web.util.DirectWebError;

import static ru.yandex.autotests.direct.web.util.beanutils.BeanHelper.getProperty;

/**
 * User: xy6er
 * Date: 28.08.13
 * Time: 17:58
 */

public class BeanFieldsSetter<B> {
    private B actualBean;
    private B expectedBean;

    public static <B> BeanFieldsSetter<B> inAccordanceWith(B expectedBean) {
        return new BeanFieldsSetter<>(expectedBean);
    }

    public BeanFieldsSetter<B> forNew(Class<B> beanClass) {
        try {
            this.actualBean = beanClass.newInstance();
        } catch (Exception e) {
            throw new DirectWebError("Не удалось создать экзепляр класса: " + e.getMessage());
        }
        return this;
    }

    public BeanFieldsSetter<B> forExisting(B actualBean) {
        this.actualBean = actualBean;
        return this;
    }

    public BeanFieldsSetter(B expectedBean) {
        this.expectedBean = expectedBean;
    }

    public BeanFieldsSetter(B actualBean, B expectedBean) {
        this.actualBean = actualBean;
        this.expectedBean = expectedBean;
    }

    public B getActualBean() {
        return actualBean;
    }

    /**
     * Устанавливает значение поля fieldName у actualBean.
     * Значение поля вычисляется методом argument у объекта F object
     *
     * @param fieldName Имя поля в устанавливаемом бине
     * @param object    объект для вычисления значения поля(В большинстве случаев HtmlElement либо PageObject)
     * @param argument  Лямбда выражение вида on(BeanClass.class).getFieldNameValue()
     * @param <F>       Тип объекта, предоставляющего значение поля
     * @param <T>       Тип люмбда-выражения argument
     * @return
     */
    public <F, T> BeanFieldsSetter<B> set(String fieldName, F object, T argument) {
        if (getProperty(expectedBean, fieldName) != null) {
            if (argument.getClass().equals(Float.class)) {
                throw new DirectWebError("Нельзя использовать Float поля или методы в BeanFieldsSetter");
            }
            Converter<F, T> converter = new ArgumentConverter<>(argument);
            BeanHelper.setNotNullProperty(actualBean, fieldName, converter.convert(object));
        }
        return this;
    }

    /**
     * Устанавливает значение поля fieldName у actualBean.
     * Значение поля вычисляется методом argument у объекта F object
     *
     * @param fieldName     Имя поля в устанавливаемом бине
     * @param valueProducer лямбда вычисляющая значение поля
     * @return
     */
    public BeanFieldsSetter<B> set(String fieldName, Supplier valueProducer) {
        if (getProperty(expectedBean, fieldName) != null) {
            BeanHelper.setNotNullProperty(actualBean, fieldName, valueProducer.get());
        }
        return this;
    }

    @Deprecated
    public <F, T> BeanFieldsSetter<B> setNotNullProperty(String fieldName, F object, T argument) {
        return this.set(fieldName, object, argument);
    }
}