/*******************************************************************************
 * Copyright (c) 2010, 2015 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Yuriy Vashchuk (yvashchuk@gmail.com) - Initial API and implementation
 *   Patrick Tasse - Refactoring
 *   Vincent Perot - Add subfield filtering
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.core.filter.model;

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

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;

/**
 * The base class for the Filter tree nodes
 *
 * @version 1.0
 * @author Yuriy Vashchuk
 * @author Patrick Tasse
 */
public abstract class TmfFilterTreeNode implements ITmfFilterTreeNode, Cloneable {

    private static final String[] VALID_CHILDREN = {
            TmfFilterTraceTypeNode.NODE_NAME,
            TmfFilterAndNode.NODE_NAME,
            TmfFilterOrNode.NODE_NAME,
            TmfFilterContainsNode.NODE_NAME,
            TmfFilterEqualsNode.NODE_NAME,
            TmfFilterMatchesNode.NODE_NAME,
            TmfFilterCompareNode.NODE_NAME
    };

    private ITmfFilterTreeNode parent = null;
    private ArrayList<ITmfFilterTreeNode> children = new ArrayList<>();

    /**
     * @param parent
     *            the parent node
     */
    public TmfFilterTreeNode(final ITmfFilterTreeNode parent) {
        if (parent != null) {
            parent.addChild(this);
        }
    }

    @Override
    public ITmfFilterTreeNode getParent() {
        return parent;
    }

    @Override
    public abstract String getNodeName();

    @Override
    public boolean hasChildren() {
        return (!children.isEmpty());
    }

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

    @Override
    public @NonNull ITmfFilterTreeNode[] getChildren() {
        return children.toArray(new @NonNull ITmfFilterTreeNode[0]);
    }

    @Override
    public ITmfFilterTreeNode getChild(final int index) throws IndexOutOfBoundsException {
        return children.get(index);
    }

    @Override
    public ITmfFilterTreeNode remove() {
        if (getParent() != null) {
            getParent().removeChild(this);
        }
        return this;
    }

    @Override
    public ITmfFilterTreeNode removeChild(ITmfFilterTreeNode node) {
        children.remove(node);
        node.setParent(null);
        return node;
    }

    @Override
    public int addChild(final ITmfFilterTreeNode node) {
        node.setParent(this);
        if (children.add(node)) {
            return (children.size() - 1);
        }
        return -1;
    }

    @Override
    public ITmfFilterTreeNode replaceChild(final int index, final ITmfFilterTreeNode node) throws IndexOutOfBoundsException {
        node.setParent(this);
        return children.set(index, node);
    }

    @Override
    public void setParent(final ITmfFilterTreeNode parent) {
        this.parent = parent;
    }

    @Override
    public abstract boolean matches(ITmfEvent event);

    @Override
    public List<String> getValidChildren() {
        return Arrays.asList(VALID_CHILDREN);
    }

    @Override
    public ITmfFilterTreeNode clone() {
        try {
            TmfFilterTreeNode clone = (TmfFilterTreeNode) super.clone();
            clone.parent = null;
            clone.children = new ArrayList<>(children.size());
            for (ITmfFilterTreeNode child : getChildren()) {
                clone.addChild(child.clone());
            }
            return clone;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    @Override
    public String toString() {
        return toString(false);
    }
}
