/*
 * Copyright 2015 Gilga Einziger. All Rights Reserved.
 *
 * 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 com.github.benmanes.caffeine.cache.simulator.admission.table;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.github.benmanes.caffeine.cache.simulator.BasicSettings;
import com.github.benmanes.caffeine.cache.simulator.admission.Frequency;
import com.typesafe.config.Config;

/**
 * A probabilistic multiset for estimating the popularity of an element within a time window. The
 * maximum frequency of an element. The size of the sample in relation to the cache size can be
 * controlled with a sample factor. Instead of halving the popularity of elements a random element
 * is dropped when table is full.
 *
 * This class is used to check the feasibility of using TinyTable instead of CountMin Sketch.
 *
 * @author gilg1983@gmail.com (Gil Einziger)
 */
public final class RandomRemovalFrequencyTable implements Frequency {
  /** sum of total items */
  private final int maxSum;
  /** total sum of stored items **/
  private int currSum;
  /** controls both the max count and how many items are remembered (the sum) */
  private static final int sampleFactor = 8;
  /** used to dropped items at random */
  private final Random random;
  /** a place holder for TinyTable */
  private final Map<Long, Integer> table;

  public RandomRemovalFrequencyTable(Config config) {
    BasicSettings settings = new BasicSettings(config);
    maxSum = sampleFactor * settings.maximumSize();
    random = new Random(settings.randomSeed());
    table = new HashMap<>(maxSum);
  }

  @Override
  public int frequency(long e) {
    return table.getOrDefault(e, 0);
  }

  @Override
  public void increment(long e) {
    // read and increments value
    int value = table.getOrDefault(e, 0) + 1;
    // if the value is big enough there is no point in dropping a value so we just quit
    if (value > sampleFactor) {
      return;
    }

    // putting the new value
    table.put(e, value);
    // advancing the number of items
    if (currSum < maxSum) {
      currSum++;
    }

    // Once the table is full every item that arrive some other item leaves. This implementation is
    // lacking as the probability to forget each item does not depend on frequency. (so items do not
    // converge to their true frequency. but I do not think it is worth fixing right now as this is
    // just a model.
    if (currSum == maxSum) {
      List<Long> array = new ArrayList<>(table.keySet());
      long itemToRemove = array.get(random.nextInt(array.size()));
      value = table.remove(itemToRemove);

      if (value > 1) {
        table.put(itemToRemove, value - 1);
      }
    }
  }
}
