/*
 * Copyright 2018-present Open Networking Foundation
 *
 * 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.incubator.net.resource.label;

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

import java.util.Collections;
import java.util.Objects;
import java.util.Set;

import com.google.common.annotations.Beta;
import org.onosproject.net.DeviceId;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;

/**
 * Abstraction of the capacity of device label resource or global label
 * resource. It's contiguous range of label resource. When a application apply
 * some labels of some device, first catch from Set that store
 * available labels, if the size of the Set less than the apply number, then get
 * labels by calculating with three attributes, beginLabel,endLabel and
 * currentUsedMaxLabelId.
 */
@Beta
public class LabelResourcePool {

    private final DeviceId deviceId;
    private final LabelResourceId beginLabel;
    private final LabelResourceId endLabel;
    private final long totalNum; // capacity of label resource pool
    private final long usedNum; // have used label number
    private final LabelResourceId currentUsedMaxLabelId; // the maximal label
                                                        // number id
    private ImmutableSet<LabelResource> releaseLabelId; // Set of released label

    /**
     * Creates a pool by device id,begin label id,end label id.
     *
     * @param deviceId device identifier
     * @param beginLabel represents for the first label id in the range of label
     *            resource pool
     * @param endLabel represents for the last label id in the range of label
     *            resource pool
     */
    public LabelResourcePool(String deviceId, long beginLabel, long endLabel) {
        this(deviceId, beginLabel, endLabel, endLabel - beginLabel + 1, 0L,
             beginLabel, ImmutableSet.copyOf(Collections.emptySet()));
    }

    /**
     * Creates a pool by device id,begin label id,end label id.
     * Used to update a pool in the store.
     *
     * @param deviceId device identifier
     * @param beginLabel represents for the first label id in the range of label
     *            resource pool
     * @param endLabel represents for the last label id in the range of label
     *            resource pool
     * @param totalNum capacity of label resource pool
     * @param usedNum have used label number
     * @param currentUsedMaxLabelId the maximal label number id
     * @param releaseLabelId Set of released label
     */
    public LabelResourcePool(String deviceId, long beginLabel, long endLabel,
                             long totalNum, long usedNum,
                             long currentUsedMaxLabelId,
                             ImmutableSet<LabelResource> releaseLabelId) {
        checkArgument(endLabel >= beginLabel,
                      "endLabel %s must be greater than or equal to beginLabel %s",
                      endLabel, beginLabel);
        this.deviceId = DeviceId.deviceId(deviceId);
        this.beginLabel = LabelResourceId.labelResourceId(beginLabel);
        this.endLabel = LabelResourceId.labelResourceId(endLabel);
        this.totalNum = totalNum;
        this.usedNum = usedNum;
        this.currentUsedMaxLabelId = LabelResourceId
                .labelResourceId(currentUsedMaxLabelId);
        this.releaseLabelId = releaseLabelId;
    }

    /**
     * Returns a device id.
     *
     * @return DeviceId
     */
    public DeviceId deviceId() {
        return deviceId;
    }

    /**
     * Returns a begin Label id.
     *
     * @return begin Label id
     */
    public LabelResourceId beginLabel() {
        return beginLabel;
    }

    /**
     * Returns an end Label id.
     *
     * @return end Label id
     */
    public LabelResourceId endLabel() {
        return endLabel;
    }

    /**
     * Returns a begin Label id.
     *
     * @return current Used Maximal Label Id
     */
    public LabelResourceId currentUsedMaxLabelId() {
        return currentUsedMaxLabelId;
    }

    /**
     * Returns total number.
     *
     * @return the total label number
     */
    public long totalNum() {
        return totalNum;
    }

    /**
     * Returns used number.
     *
     * @return the used label number
     */
    public long usedNum() {
        return usedNum;
    }

    /**
     * Returns the Set of released label before.
     *
     * @return the Set of LabelResource
     */
    public Set<LabelResource> releaseLabelId() {
        return releaseLabelId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.deviceId, this.beginLabel, this.endLabel,
                            this.totalNum, this.usedNum,
                            this.currentUsedMaxLabelId, this.releaseLabelId);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof LabelResourcePool) {
            LabelResourcePool that = (LabelResourcePool) obj;
            return Objects.equals(this.deviceId, that.deviceId)
                    && Objects.equals(this.beginLabel, that.beginLabel)
                    && Objects.equals(this.endLabel, that.endLabel)
                    && Objects.equals(this.totalNum, that.totalNum)
                    && Objects.equals(this.usedNum, that.usedNum)
                    && Objects.equals(this.currentUsedMaxLabelId,
                                      that.currentUsedMaxLabelId)
                    && Objects.equals(this.releaseLabelId, that.releaseLabelId);
        }
        return false;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return MoreObjects.toStringHelper(this).add("deviceId", this.deviceId)
                .add("beginLabel", this.beginLabel)
                .add("endLabel", this.endLabel).add("totalNum", this.totalNum)
                .add("usedNum", this.usedNum)
                .add("currentUsedMaxLabelId", this.currentUsedMaxLabelId)
                .add("releaseLabelId", this.releaseLabelId).toString();
    }
}
