/*
 * Copyright 2015 Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.net.newresource;

import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * An object that is used to locate a resource in a network.
 * A ResourcePath represents a path that is hierarchical and composed of a sequence
 * of elementary resources that are not globally identifiable. A ResourcePath can be a globally
 * unique resource identifier.
 *
 * Two types of resource are considered. One is discrete type and the other is continuous type.
 * Discrete type resource is a resource whose amount is measured as a discrete unit. VLAN ID and
 * MPLS label are examples of discrete type resource. Continuous type resource is a resource whose
 * amount is measured as a continuous value. Bandwidth is an example of continuous type resource.
 * A double value is associated with a continuous type value.
 *
 * Users of this class must keep the semantics of resources regarding the hierarchical structure.
 * For example, resource path, Link:1/VLAN ID:100, is valid, but resource path, VLAN ID:100/Link:1
 * is not valid because a link is not a sub-component of a VLAN ID.
 */
@Beta
public abstract class ResourcePath {

    private final Discrete parent;
    private final Object last;

    public static final Discrete ROOT = new Discrete();

    /**
     * Creates an resource path which represents a discrete-type resource from the specified components.
     *
     * @param components components of the path. The order represents hierarchical structure of the resource.
     */
    public static ResourcePath discrete(Object... components) {
        if (components.length == 0) {
            return ROOT;
        } else {
            return new Discrete(ImmutableList.copyOf(components));
        }
    }

    /**
     * Creates an resource path which represents a continuous-type resource from the specified components.
     *
     * @param value amount of the resource
     * @param components components of the path. The order represents hierarchical structure of the resource.
     */
    public static ResourcePath continuous(double value, Object... components) {
        return new Continuous(ImmutableList.copyOf(components), value);
    }

    /**
     * Creates an resource path from the specified components.
     *
     * @param components components of the path. The order represents hierarchical structure of the resource.
     */
    ResourcePath(List<Object> components) {
        checkNotNull(components);
        checkArgument(!components.isEmpty());

        LinkedList<Object> children = new LinkedList<>(components);
        this.last = children.pollLast();
        if (children.isEmpty()) {
            this.parent = ROOT;
        } else {
            this.parent = new Discrete(children);
        }
    }

    /**
     * Creates an resource path from the specified parent and child.
     *
     * @param parent the parent of this resource
     * @param last a child of the parent
     */
    ResourcePath(Discrete parent, Object last) {
        checkNotNull(parent);
        checkNotNull(last);

        this.parent = parent;
        this.last = last;
    }

    // for serialization
    private ResourcePath() {
        this.parent = null;
        this.last = null;
    }

    /**
     * Returns the components of this resource path.
     *
     * @return the components of this resource path
     */
    public List<Object> components() {
        LinkedList<Object> components = new LinkedList<>();

        ResourcePath current = this;
        while (current.parent().isPresent()) {
            components.addFirst(current.last);
            current = current.parent;
        }

        return components;
    }

    /**
     * Returns the parent resource path of this instance.
     * E.g. if this path is Link:1/VLAN ID:100, the return value is the resource path for Link:1.
     *
     * @return the parent resource path of this instance.
     * If there is no parent, empty instance will be returned.
     */
    public Optional<Discrete> parent() {
        return Optional.ofNullable(parent);
    }

    public ResourcePath child(Object child) {
        checkState(this instanceof Discrete);

        return new Discrete((Discrete) this, child);
    }

    public ResourcePath child(Object child, double value) {
        checkState(this instanceof Discrete);

        return new Continuous((Discrete) this, child, value);
    }

    /**
     * Returns the last component of this instance.
     *
     * @return the last component of this instance.
     * The return value is equal to the last object of {@code components()}.
     */
    public Object last() {
        return last;
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.parent, this.last);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof ResourcePath)) {
            return false;
        }
        final ResourcePath that = (ResourcePath) obj;
        return Objects.equals(this.parent, that.parent)
                && Objects.equals(this.last, that.last);
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("parent", parent)
                .add("last", last)
                .toString();
    }

    /**
     * Represents a resource path which specifies a resource which can be measured
     * as a discrete unit. A VLAN ID and a MPLS label of a link are examples of the resource.
     * <p>
     * Note: This class is exposed to the public, but intended to be used in the resource API
     * implementation only. It is not for resource API user.
     * </p>
     */
    public static final class Discrete extends ResourcePath {
        private Discrete() {
            super();
        }

        private Discrete(List<Object> components) {
            super(components);
        }

        private Discrete(Discrete parent, Object last) {
            super(parent, last);
        }
    }

    /**
     * Represents a resource path which specifies a resource which can be measured
     * as continuous value. Bandwidth of a link is an example of the resource.
     * <p>
     * Note: This class is exposed to the public, but intended to be used in the resource API
     * implementation only. It is not for resource API user.
     */
    public static final class Continuous extends ResourcePath {
        // Note: value is not taken into account for equality
        private final double value;

        private Continuous(List<Object> components, double value) {
            super(components);
            this.value = value;
        }

        public Continuous(Discrete parent, Object last, double value) {
            super(parent, last);
            this.value = value;
        }

        /**
         * Returns the value of the resource amount.
         *
         * @return the value of the resource amount
         */
        public double value() {
            return value;
        }
    }
}

@@ -2474,32 +2474,70 @@
 f the resource.%0A
+     * @return resource path instance%0A
      */%0A    publ
@@ -3023,32 +3023,70 @@
 f the resource.%0A
+     * @return resource path instance%0A
      */%0A    publ

Fix Javadoc warnings