package org.asciidoctor.jruby.internal;

import org.asciidoctor.jruby.ast.impl.ContentNodeImpl;
import org.asciidoctor.jruby.ast.impl.NodeConverter;
import org.jruby.RubyArray;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;


public class RubyBlockListDecorator<T> extends AbstractList<T> {

    private final RubyArray rubyBlockList;

    public RubyBlockListDecorator(RubyArray rubyBlockList) {
        this.rubyBlockList = rubyBlockList;
    }

    @Override
    public int size() {
        return rubyBlockList.size();
    }

    @Override
    public boolean contains(Object o) {
        if (!(o instanceof ContentNodeImpl)) {
            return false;
        }
        return rubyBlockList.contains(((ContentNodeImpl) o).getRubyObject());
    }

    @Override
    public boolean add(T abstractBlock) {
        return rubyBlockList.add(((ContentNodeImpl) abstractBlock).getRubyObject());
    }

    @Override
    public boolean remove(Object o) {
        if (o instanceof IRubyObject) {
            return rubyBlockList.remove(o);
        }
        if (!(o instanceof ContentNodeImpl)) {
            return false;
        }
        return rubyBlockList.remove(((ContentNodeImpl) o).getRubyObject());
    }

    private Collection<Object> getDelegateCollection(Collection<?> c) {
        Collection<Object> delegateList = new ArrayList<Object>(c.size());
        for (Object o: c) {
            if (o instanceof ContentNodeImpl) {
                delegateList.add(((ContentNodeImpl) o).getRubyObject());
            } else {
                delegateList.add(o);
            }
        }
        return delegateList;
    }

    @Override
    public void clear() {
        rubyBlockList.clear();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof RubyBlockListDecorator) {
            return rubyBlockList.equals(((RubyBlockListDecorator) o).rubyBlockList);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return rubyBlockList.hashCode();
    }

    @Override
    public T get(int index) {
        return (T) NodeConverter.createASTNode(rubyBlockList.get(index));
    }

    @Override
    public T set(int index, T element) {
        Object oldObject;
        if (element instanceof IRubyObject) {
            oldObject = rubyBlockList.set(index, element);
        } else {
            oldObject = rubyBlockList.set(index, ((ContentNodeImpl) element).getRubyObject());
        }
        return (T) NodeConverter.createASTNode(oldObject);
    }

    @Override
    public void add(int index, T element) {
        if (element instanceof IRubyObject) {
            rubyBlockList.set(index, element);
        } else {
            rubyBlockList.add(index, ((ContentNodeImpl) element).getRubyObject());
        }
    }

    @Override
    public T remove(int index) {
        Object oldObject = rubyBlockList.remove(index);
        if (oldObject == null) {
            return null;
        } else {
            return (T) NodeConverter.createASTNode(oldObject);
        }
    }

    @Override
    public int indexOf(Object o) {
        if (o instanceof IRubyObject) {
            return rubyBlockList.indexOf(o);
        } else if (o instanceof ContentNodeImpl) {
            return rubyBlockList.indexOf(((ContentNodeImpl) o).getRubyObject());
        } else {
            return -1;
        }
    }

    @Override
    public int lastIndexOf(Object o) {
        if (o instanceof IRubyObject) {
            return rubyBlockList.lastIndexOf(o);
        } else if (o instanceof ContentNodeImpl) {
            return rubyBlockList.lastIndexOf(((ContentNodeImpl) o).getRubyObject());
        } else {
            return -1;
        }
    }
}