package net.ranides.assira.ui.query;

import javax.accessibility.AccessibleContext;
import net.ranides.assira.generic.Ref;
import net.ranides.assira.reflection.*;

import java.awt.*;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

class RComponent implements IComponent {

    private final Component component;

    private final BeanModel model;

    private final BeanModel.FluentMap fluent;

    RComponent(Component component) {
        this.component = component;
        this.model = BeanModel.typefor(component);
        this.fluent = model.fluent(component);
    }

    @Override
    public IClass<?> type() {
        return model.type();
    }

    @Override
    public IComponent parent() {
        return new RComponent(component.getParent());
    }

    @Override
    public IComponent root() {
        Component out = component;
        while(out.getParent() != null) {
            out = out.getParent();
        }
        return new RComponent(out);
    }

    @Override
    public String name() {
        return component.getName();
    }

    @Override
    public AccessibleContext accessible() {
        return component.getAccessibleContext();
    }

    @Override
    public Map<String, Object> properties() {
        return fluent;
    }

    @Override
    public BeanPropertyBinding<Object> property(String name) {
        BeanPropertyBinding<Object> binding = fluent.getBinding(name);
        if(binding == null) {
            throw new IllegalArgumentException("Unknown property " + component + ":" + name);
        }
        return binding;
    }

    @Override
    public void property(String name, Object value) {
        fluent.put(name, value);
    }

    @Override
    public void set(String name, Object value) {
        property(name).set(value);
    }

    @Override
    public Object get(String name) {
        return property(name).get();
    }

    @Override
    public IComponents children() {
        if(component instanceof Container) {
            synchronized (component.getTreeLock()) {
                return new RComponents(((Container) component).getComponents());
            }
        }
        return RComponents.EMPTY;
    }

    @Override
    public Ref<IComponent> child(int index) {
        if(component instanceof Container) {
            Container container = (Container) this.component;
            synchronized (component.getTreeLock()) {
                if (index < 0 || index >= container.getComponentCount()) {
                    return Ref.of(new RComponent(container.getComponent(index)));
                }
            }
        }
        return Ref.empty();
    }

    @Override
    public Ref<IComponent> child(String name) {
        if(component instanceof Container) {
            Container container = (Container) this.component;
            synchronized (component.getTreeLock()) {
                for(Component c : container.getComponents()) {
                    if(name.equals(c.getName())) {
                        return Ref.of(new RComponent(c));
                    }
                }
            }
        }
        return Ref.empty();
    }

    @Override
    public IComponents find(String[] path) {
        IComponents that = IComponents.of(this);

        for (String name : path) {
            that = that.find(c -> name.equals(c.name()));
        }

        return that;
    }

    @Override
    public IComponents find(String path) {
        String[] component = IComponentSelector.of(this, path).path();
        return find(component);
    }

    @Override
    public IComponents find(Predicate<IComponent> visitor) {
        if(visitor.test(this)) {
            return IComponents.of(this);
        }

        if(!isContainer()) {
            return RComponents.EMPTY;
        }

        return new RComponents(((Container)component).getComponents()).find(visitor);
    }

    @Override
    public boolean isContainer() {
        return component instanceof Container;
    }

    @Override
    public boolean isType(Class<?> type) {
        return type.isInstance(component);
    }

    @Override
    public Component unwrap() {
        return component;
    }

    @Override
    public <T extends Component> T unwrap(Class<T> type) {
        return type.cast(component);
    }

    @Override
    public void add(Component component) {
        if(!isContainer()) {
            throw new IllegalArgumentException(this + " is not container");
        }
        ((Container)this.component).add(component);
    }

    @Override
    public void add(IComponent component) {
        add(component.unwrap());
    }

    @Override
    public String toString() {
        return "{" + component.getClass() + ":" + component.getName() + "}";
    }
}
