/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.concurrent.collection.impl;

import com.crunii.micro.common.concurrent.collection.FixSizeSortedSet;
import com.crunii.micro.common.concurrent.collection.RemoveId;
import com.google.common.base.Objects;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @author 田平 create 2022-07-27 14:31:17
 */
public class FixSizeTreeSet<T extends Serializable & RemoveId & Comparable> implements FixSizeSortedSet<T> {

	private TreeSet<InnerElement> treeSet;

	private int size;

	static class InnerElement<T extends Serializable & RemoveId & Comparable> implements Comparable<InnerElement> {
		private T element;
		private double score;

		public InnerElement(T element, double score) {
			this.element = element;
			this.score = score;
		}

		public T getElement() {
			return element;
		}

		public void setElement(T element) {
			this.element = element;
		}

		public double getScore() {
			return score;
		}

		public void setScore(double score) {
			this.score = score;
		}

		@Override
		public int compareTo(InnerElement o) {
			if (this.getScore() == o.getScore()) {
				return this.element.compareTo(o.getElement());
			} else {
				return Double.compare(this.getScore(), o.getScore());
			}
		}

		@Override
		public String toString() {
			return "InnerElement{" +
					"element=" + element +
					", score=" + score +
					'}';
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) {
				return true;
			}
			if (o == null || getClass() != o.getClass()) {
				return false;
			}
			InnerElement<?> innerElement1 = (InnerElement<?>) o;
			return Double.compare(innerElement1.score, score) == 0 &&
					Objects.equal(element, innerElement1.element);
		}

		@Override
		public int hashCode() {
			return Objects.hashCode(element, score);
		}
	}

	public FixSizeTreeSet(int size) {
		this.size = size;
		this.treeSet = new TreeSet<>();
		Assert.isTrue(size > 0, "size must be more than zero!");
	}

	@Override
	public synchronized List<T> getAll() {
		return treeSet.stream().map(e -> (T) (e.getElement())).collect(Collectors.toList());
	}

	@Override
	public synchronized void add(T element, double score) {
		treeSet.add(new InnerElement(element, score));
		if (treeSet.size() > size) {
			treeSet.pollFirst();
		}
	}

	public synchronized boolean removeById(String removeId) {
		return treeSet.removeIf(e -> {
			RemoveId eRemoveId = (RemoveId) e.getElement();
			return eRemoveId.getRemoveId().equals(removeId);
		});
	}

	@Override
	public synchronized void addAll(Map<T, Double> elements) {
		if (!elements.isEmpty()) {
			List<InnerElement> es = elements.entrySet()
					.stream()
					.map(e -> new InnerElement(e.getKey(), e.getValue()))
					.collect(Collectors.toList());
			treeSet.addAll(es);
			int removeSize = treeSet.size() - size;
			if (removeSize > 0) {
				for (int i = 0; i < removeSize; i++) {
					treeSet.pollFirst();
				}
			}
		}
	}

	@Override
	public synchronized void clear() {
		treeSet.clear();
	}

	public static void main(String[] args) {
		FixSizeTreeSet<SimpleRemoveId> set = new FixSizeTreeSet(10);
		Map<SimpleRemoveId, Double> map = new HashMap<>();
		map.put(new SimpleRemoveId("4"), 4D);
		map.put(new SimpleRemoveId("3"), 3D);
		map.put(new SimpleRemoveId("2"), 2D);
		map.put(new SimpleRemoveId("1"), 1D);
		map.put(new SimpleRemoveId("1"), 1D);
		map.put(new SimpleRemoveId("5"), 5D);
		map.put(new SimpleRemoveId("9"), 5D);
		map.put(new SimpleRemoveId("10"), 6D);
		set.addAll(map);
		System.out.println(set.removeById("4100"));
		System.out.println(set.removeById("5100"));
		set.getAll().forEach(System.out::println);
	}
}
