/*
 * 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 {

    /**
     * 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;
    }
}
