/*
 * Copyright 2015-2024 MicroEJ Corp. All rights reserved.
 * This library is provided in source code for use, modification and test, subject to license terms.
 * Any modification of the source code will break MicroEJ Corp. warranties on the whole library.
 */
package ej.mwt.stylesheet;

import ej.mwt.Widget;
import ej.mwt.style.Style;

/**
 * Wraps a stylesheet and provides a cache to avoid having several instances of the same style.
 * <p>
 * When a style is requested for a given widget, its style is retrieved from the wrapped stylesheet. If an equal style
 * is available in the cache, it is returned. Otherwise, the style is added to the cache.
 */
public class CachedStylesheet implements Stylesheet {

	private static final int DEFAULT_SIZE = 10;

	private final Stylesheet stylesheet;
	private final Style[] stylesCache;
	private byte stylesCount;

	/**
	 * Creates a cached stylesheet with the given stylesheet to wrap and the default cache size.
	 *
	 * @param stylesheet
	 *            the stylesheet to wrap.
	 */
	public CachedStylesheet(Stylesheet stylesheet) {
		this(stylesheet, DEFAULT_SIZE);
	}

	/**
	 * Creates a cached stylesheet with the given stylesheet to wrap and the given cache size.
	 *
	 * @param stylesheet
	 *            the stylesheet to wrap.
	 * @param size
	 *            the size of the cache.
	 */
	public CachedStylesheet(Stylesheet stylesheet, int size) {
		this.stylesheet = stylesheet;
		this.stylesCache = new Style[size];
	}

	@Override
	public Style getStyle(Widget widget) {
		Style style = this.stylesheet.getStyle(widget);
		int styleHashCode = style.hashCode();

		Style[] stylesCache = this.stylesCache;
		byte stylesCount = this.stylesCount;
		for (int i = 0; i < stylesCount; i++) {
			Style cachedStyle = stylesCache[i];
			assert cachedStyle != null;
			if (styleHashCode == cachedStyle.hashCode() && style.equals(cachedStyle)) {
				if (i != 0) {
					// Bring this style forward.
					stylesCache[i] = stylesCache[i - 1];
					stylesCache[i - 1] = cachedStyle;
				}
				return cachedStyle;
			}
		}
		// Not found add it at the center.
		int half = stylesCount >> 1;
		System.arraycopy(stylesCache, half, stylesCache, half + 1,
				Math.min(stylesCount, stylesCache.length - 1) - half);
		stylesCache[half] = style;
		if (stylesCount != stylesCache.length) {
			++stylesCount;
		}
		this.stylesCount = stylesCount;
		return style;
	}
}
