/*
 * 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.style.background;

import ej.annotation.Nullable;
import ej.microui.display.GraphicsContext;
import ej.microui.display.Image;
import ej.microui.display.Painter;
import ej.mwt.util.Alignment;

/**
 * Draws a repeated image on the background.
 */
public class TiledImageBackground implements Background {

    /**
     * Creates a tiled image background aligned on the top-left corner and repeated horizontally and vertically.
     *
     * @param image
     *            the background image.
     */
    public TiledImageBackground(Image image) {
        this(image, Alignment.LEFT, Alignment.TOP, true, true);
    }

    /**
     * Creates a tiled image background specifying its attributes.
     *
     * @param image
     *            the background image.
     * @param horizontalAlignment
     *            the horizontal alignment.
     * @param verticalAlignment
     *            the vertical alignment.
     * @param horizontalRepeat
     *            <code>true</code> if the image is repeated horizontally, <code>false</code> otherwise.
     * @param verticalRepeat
     *            <code>true</code> if the image is repeated vertically, <code>false</code> otherwise.
     * @throws IllegalArgumentException
     *             if the horizontal or vertical alignment is not valid.
     * @see Alignment#validateHorizontalAlignment(int)
     * @see Alignment#validateVerticalAlignment(int)
     */
    public TiledImageBackground(Image image, int horizontalAlignment, int verticalAlignment, boolean horizontalRepeat, boolean verticalRepeat) {
        Alignment.validateHorizontalAlignment(horizontalAlignment);
        Alignment.validateVerticalAlignment(verticalAlignment);
        this.image = image;
        this.horizontalAlignment = (byte) horizontalAlignment;
        this.verticalAlignment = (byte) verticalAlignment;
        this.horizontalRepeat = horizontalRepeat;
        this.verticalRepeat = verticalRepeat;
    }

    @Override
    public boolean isTransparent(int width, int height) {
        Image image = this.image;
        return (image.isTransparent() || checkTransparent(this.horizontalRepeat, image.getWidth(), width) || checkTransparent(this.verticalRepeat, image.getHeight(), height));
    }

    @Override
    public void apply(GraphicsContext g, int width, int height) {
        Image image = this.image;
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        boolean horizontalRepeat = this.horizontalRepeat;
        boolean verticalRepeat = this.verticalRepeat;
        int horizontalAlignment = this.horizontalAlignment;
        int verticalAlignment = this.verticalAlignment;
        // compute number of repetitions
        int hrepeat = getRepeatCount(horizontalRepeat, width, imageWidth);
        int vrepeat = getRepeatCount(verticalRepeat, height, imageHeight);
        // compute position of first repetition
        int xStart;
        int yStart;
        if (horizontalAlignment == Alignment.HCENTER) {
            if (horizontalRepeat) {
                // one more because both sides can be cut
                hrepeat++;
            }
            xStart = ((width - imageWidth) >> 1) - imageWidth * (hrepeat >> 1);
        } else if (horizontalAlignment == Alignment.RIGHT) {
            xStart = width - imageWidth * hrepeat;
        } else {
            // left or undefined
            xStart = 0;
        }
        if (verticalAlignment == Alignment.VCENTER) {
            if (verticalRepeat) {
                // one more because both sides can be cut
                vrepeat++;
            }
            yStart = ((height - imageHeight) >> 1) - imageHeight * (vrepeat >> 1);
        } else if (verticalAlignment == Alignment.BOTTOM) {
            yStart = height - imageHeight * vrepeat;
        } else {
            // top or undefined
            yStart = 0;
        }
        // draw all image repetitions
        int currentX = xStart;
        for (int i = 0; i < hrepeat; i++) {
            int currentY = yStart;
            for (int j = 0; j < vrepeat; j++) {
                Painter.drawImage(g, image, currentX, currentY);
                currentY += imageHeight;
            }
            currentX += imageWidth;
        }
        // remove background color
        g.removeBackgroundColor();
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (obj != null && getClass() == obj.getClass()) {
            TiledImageBackground background = (TiledImageBackground) obj;
            return this.image == background.image && this.horizontalAlignment == background.horizontalAlignment && this.verticalAlignment == background.verticalAlignment && this.horizontalRepeat == background.horizontalRepeat && this.verticalRepeat == background.verticalRepeat;
        }
        return false;
    }

    @Override
    public int hashCode() {
        // don't call this.image.hashCode() to avoid embedding the hashCode() method of every class (M0081MEJA-1240)
        return this.horizontalAlignment * this.verticalAlignment * (this.horizontalRepeat ? -3 : 3) * (this.verticalRepeat ? -5 : 5);
    }
}
