package fr.ourten.teabeans.value;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import fr.ourten.teabeans.listener.ListValueChangeListener;

public class BaseListProperty<T> extends BaseProperty<List<T>> implements ListProperty<T>
{
    private BiFunction<T, T, T>                                 checker;

    /**
     * The list of attached listeners that need to be notified when the value
     * change.
     */
    private final ArrayList<ListValueChangeListener<? super T>> listValueChangeListeners;

    public BaseListProperty(final List<T> value, final String name)
    {
        super(value, name);
        this.listValueChangeListeners = Lists.newArrayList();

        this.value = value != null ? Lists.newArrayList(value) : Lists.newArrayList();
    }

    public BaseListProperty(final List<T> value)
    {
        this(value, "");
    }

    @Override
    public ImmutableList<T> getValue()
    {
        return ImmutableList.copyOf(this.value);
    }

    @Override
    public void addListener(final ListValueChangeListener<? super T> listener)
    {
        if (!this.listValueChangeListeners.contains(listener))
            this.listValueChangeListeners.add(listener);
    }

    @Override
    public void removeListener(final ListValueChangeListener<? super T> listener)
    {
        this.listValueChangeListeners.remove(listener);
    }

    public BiFunction<T, T, T> getElementChecker()
    {
        return this.checker;
    }

    public void setElementChecker(final BiFunction<T, T, T> checker)
    {
        this.checker = checker;
    }

    @Override
    public T get(final int index)
    {
        return this.value.get(index);
    }

    private void add(T element, Function<T, T> action)
    {
        this.fireInvalidationListeners();
        this.fireListChangeListeners(null, element);

        List<T> old = null;
        if (!this.valueChangeListeners.isEmpty())
            old = Lists.newArrayList(this.value);

        if (this.checker != null)
            element = this.checker.apply(null, element);

        action.apply(element);

        this.fireChangeListeners(old, this.value);
    }

    @Override
    public void add(final T element)
    {
        add(element, (T e) ->
        {
            this.value.add(e);
            return e;
        });
    }

    @Override
    public void add(final int index, final T element)
    {
        add(element, (T e) ->
        {
            this.value.add(index, e);
            return e;
        });
    }

    @Override
    public void addAll(final Collection<T> elements)
    {
        elements.forEach(this::add);
    }

    @Override
    public T remove(final int index)
    {
        this.fireInvalidationListeners();
        this.fireListChangeListeners(this.value.get(index), null);

        List<T> old = null;
        if (!this.valueChangeListeners.isEmpty())
            old = Lists.newArrayList(this.value);
        final T rtn = this.value.remove(index);
        this.fireChangeListeners(old, this.value);

        return rtn;
    }

    @Override
    public boolean contains(final T element)
    {
        return this.value.contains(element);
    }

    @Override
    public void set(final int index, T element)
    {
        this.fireInvalidationListeners();
        this.fireListChangeListeners(this.value.get(index), element);

        List<T> old = null;
        if (!this.valueChangeListeners.isEmpty())
            old = Lists.newArrayList(this.value);

        if (this.checker != null)
            element = this.checker.apply(this.value.get(index), element);

        this.value.set(index, element);
        this.fireChangeListeners(old, this.value);
    }

    @Override
    public int indexOf(final T element)
    {
        return this.value.indexOf(element);
    }

    @Override
    public void clear()
    {
        this.setValue(Lists.newArrayList());
    }

    @Override
    public void sort(final Comparator<? super T> comparator)
    {
        final ArrayList<T> temp = Lists.newArrayList(this.value);
        temp.sort(comparator);

        this.fireChangeListeners(this.value, temp);
        this.setValue(temp);
    }

    private void fireListChangeListeners(final T oldValue, final T newValue)
    {
        for (final ListValueChangeListener<? super T> listener : this.listValueChangeListeners)
            listener.valueChanged(this, oldValue, newValue);
    }

    @Override
    public int size()
    {
        return this.getValue().size();
    }
}
@@ -201,24 +201,24 @@
 unction.
-Function
+Consumer
 ;%0A%0Aimpor
@@ -1918,28 +1918,31 @@
 lement, 
-Function%3CT, 
+final Consumer%3C
 T%3E actio
@@ -2283,20 +2283,21 @@
 action.a
-pply
+ccept
 (element
@@ -2415,32 +2415,37 @@
 )%0A    %7B%0A        
+this.
 add(element, (T 
@@ -2444,39 +2444,8 @@
 ent,
- (T e) -%3E%0A        %7B%0A           
  thi
@@ -2455,48 +2455,13 @@
 alue
-.
+::
 add
-(e);%0A            return e;%0A        %7D
 );%0A 
@@ -2548,16 +2548,21 @@
         
+this.
 add(elem
@@ -2569,39 +2569,13 @@
 ent,
- (T
  e
-)
  -%3E
-%0A        %7B%0A           
  thi
@@ -2599,41 +2599,8 @@
 , e)
-;%0A            return e;%0A        %7D
 );%0A 

Change list internal function to a consumer