/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.planimod.ui.databinding;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
import org.eclipse.core.databinding.property.INativePropertyListener;
import org.eclipse.core.databinding.property.ISimplePropertyListener;
import org.eclipse.core.databinding.property.NativePropertyListener;
import org.eclipse.core.databinding.property.value.SimpleValueProperty;

/**
 * Property used to check if a set contains a given value.
 * 
 * @author Patrik Dufresne
 * 
 */
public class SetContainsPropertyValue extends SimpleValueProperty {

    private class Listener extends NativePropertyListener implements ISetChangeListener {

        List<Object> knownValue;

        public Listener(ISimplePropertyListener listener) {
            super(SetContainsPropertyValue.this, listener);
        }

        @Override
        protected void doAddTo(Object source) {
            if (this.knownValue == null) {
                // Add listener to the set
                this.knownValue = new ArrayList<Object>();
                SetContainsPropertyValue.this.set.addSetChangeListener(this);
            }
            this.knownValue.add(source);
        }

        @Override
        protected void doRemoveFrom(Object source) {
            if (this.knownValue != null) {
                this.knownValue.remove(source);
                if (this.knownValue.size() == 0) {
                    this.knownValue = null;
                    SetContainsPropertyValue.this.set.removeSetChangeListener(this);
                }
            }
        }

        @Override
        public void handleSetChange(SetChangeEvent event) {
            for (Object val : event.diff.getAdditions()) {
                if (this.knownValue.contains(val)) {
                    fireChange(val, Diffs.createValueDiff(Boolean.FALSE, Boolean.TRUE));
                }
            }
            for (Object val : event.diff.getRemovals()) {
                if (this.knownValue.contains(val)) {
                    fireChange(val, Diffs.createValueDiff(Boolean.TRUE, Boolean.FALSE));
                }
            }
        }

    }

    IObservableSet set;

    public SetContainsPropertyValue(IObservableSet set) {
        this.set = set;
    }

    @Override
    public Object getValueType() {
        return Boolean.TYPE;
    }

    @Override
    protected Object doGetValue(Object source) {
        return Boolean.valueOf(this.set.contains(source));
    }

    @Override
    protected void doSetValue(Object source, Object value) {
        if (Boolean.TRUE.equals(value)) {
            this.set.add(source);
        } else if (Boolean.FALSE.equals(value)) {
            this.set.remove(source);
        }
    }

    @Override
    public INativePropertyListener adaptListener(ISimplePropertyListener listener) {
        return new Listener(listener);
    }

}
