package ru.dbotthepony.kstarbound.jmh;

import com.github.benmanes.caffeine.cache.Interner;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import ru.dbotthepony.kstarbound.util.HashTableInterner;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@State(Scope.Benchmark)
public class Interning {
	@Param({"1", "100", "10000", "1000000"})
	private int size;

	private StringInterner str;
	private CHMInterner chm;
	private HMInterner hm;
	private HTInterner ht;
	private CaffeineInterner ct;

	@Setup
	public void setup() {
		str = new StringInterner();
		chm = new CHMInterner();
		hm = new HMInterner();
		ht = new HTInterner();
		ct = new CaffeineInterner();
	}

	public static class StringInterner {
		public String intern(String s) {
			return s.intern();
		}
	}

	public static class HTInterner {
		private final HashTableInterner<String> interner = new HashTableInterner<>(5);

		public String intern(String s) {
			return interner.intern(s);
		}
	}

	public static class CaffeineInterner {
		private final Interner<String> interner = Interner.newWeakInterner();

		public String intern(String s) {
			return interner.intern(s);
		}
	}

	@Benchmark
	public void intern(Blackhole bh) {
		for (int c = 0; c < size; c++) {
			bh.consume(str.intern("String" + c));
		}
	}

	public static class CHMInterner {
		private final Map<String, String> map;

		public CHMInterner() {
			map = new ConcurrentHashMap<>();
		}

		public String intern(String s) {
			String exist = map.putIfAbsent(s, s);
			return (exist == null) ? s : exist;
		}
	}

	@Benchmark
	public void chm(Blackhole bh) {
		for (int c = 0; c < size; c++) {
			bh.consume(chm.intern("String" + c));
		}
	}

	public static class HMInterner {
		private final Map<String, String> map;

		public HMInterner() {
			map = new HashMap<>();
		}

		public String intern(String s) {
			String exist = map.putIfAbsent(s, s);
			return (exist == null) ? s : exist;
		}
	}

	@Benchmark
	public void hm(Blackhole bh) {
		for (int c = 0; c < size; c++) {
			bh.consume(hm.intern("String" + c));
		}
	}

	@Benchmark
	public void ht(Blackhole bh) {
		for (int c = 0; c < size; c++) {
			bh.consume(ht.intern("String" + c));
		}
	}

	@Benchmark
	public void ct(Blackhole bh) {
		for (int c = 0; c < size; c++) {
			bh.consume(ct.intern("String" + c));
		}
	}
}
