package org.araymond.joal.core.bandwith.weight;

import org.araymond.joal.core.bandwith.Peers;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.Optional.ofNullable;

public class WeightHolder<E> {

    private final Lock lock;
    private final PeersAwareWeightCalculator weightCalculator;
    private final Map<E, Weight> weightMap;
    private double totalWeight;

    public WeightHolder(final PeersAwareWeightCalculator weightCalculator) {
        this.weightCalculator = weightCalculator;
        this.weightMap = new HashMap<>();
        this.lock = new ReentrantLock();
    }

    public void addOrUpdate(final E item, final Peers peers) {
        final double weight = this.weightCalculator.calculate(peers);
        lock.lock();
        try {
            ofNullable(this.weightMap.put(item, new Weight(weight))).ifPresentOrElse(
                    previousWeight -> this.totalWeight = this.totalWeight - previousWeight.getWeight() + weight,
                    () -> this.totalWeight += weight);
        } finally {
            lock.unlock();
        }
    }

    public void remove(final E item) {
        lock.lock();
        try {
            ofNullable(this.weightMap.remove(item))
                    .ifPresent(w -> this.totalWeight -= w.getWeight());
        } finally {
            lock.unlock();
        }
    }

    /**
     * For performance reasons, this method does not benefit from the lock.
     * That's not a big deal because:
     * - if a value is not yet added it will return 0.0.
     * - if a value is still present it will return the previous value.
     */
    public double getWeightFor(final E item) {
        return ofNullable(weightMap.get(item))
                .map(Weight::getWeight)
                .orElse(0.0);
    }

    public double getTotalWeight() {
        return totalWeight;
    }

    /**
     * Wrap double to prevent unboxing
     */
    private static final class Weight {
        private final double weight;

        private Weight(final double weight) {
            this.weight = weight;
        }

        double getWeight() {
            return weight;
        }
    }
}

@@ -422,22 +422,22 @@
  Map%3CE, 
-Weight
+Double
 %3E weight
@@ -911,27 +911,16 @@
 em, 
-new Weight(
 weight))
 ).if
@@ -915,17 +915,16 @@
 weight))
-)
 .ifPrese
@@ -1023,28 +1023,16 @@
 usWeight
-.getWeight()
  + weigh
@@ -1340,20 +1340,8 @@
 -= w
-.getWeight()
 );%0A 
@@ -1760,48 +1760,8 @@
 m))%0A
-                .map(Weight::getWeight)%0A
     
@@ -1822,32 +1822,32 @@
 TotalWeight() %7B%0A
+
         return t
@@ -1867,304 +1867,7 @@
    %7D
-%0A%0A    /**%0A     * Wrap double to prevent unboxing%0A     */%0A    private static final class Weight %7B%0A        private final double weight;%0A%0A        private Weight(final double weight) %7B%0A            this.weight = weight;%0A        %7D%0A%0A        double getWeight() %7B%0A            return weight;%0A        %7D%0A    %7D
 %0A%7D%0A

remove Weight class