/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package malik.emulator.media.graphics;

import malik.emulator.util.*;

public final class RasterBuffer extends Object
{
    public static final int MAX_SIZE = 0x7fff;

    public static RasterBuffer create(int width, int height, int colorARGB, boolean opaque) {
        int len;
        int[] pixels;
        if(width < 1 || width > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент width выходит из диапазона.");
        }
        if(height < 1 || height > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент height выходит из диапазона.");
        }
        Array.fill(pixels = new int[len = width * height], 0, len, colorARGB);
        return new RasterBuffer(pixels, 0, width, width, height, opaque);
    }

    public static RasterBuffer create(int[] pixels, int offset, int scanlength, int width, int height, boolean opaque) {
        int pixelsOffset;
        int pixelsLength;
        if(pixels == null)
        {
            throw new NullPointerException("RasterBuffer.create: аргумент pixels равен нулевой ссылке.");
        }
        if(scanlength < -MAX_SIZE || scanlength > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент scanlength выходит из диапазона.");
        }
        if(width < 1 || width > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент width выходит из диапазона.");
        }
        if(height < 1 || height > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент height выходит из диапазона.");
        }
        if(scanlength >= 0)
        {
            pixelsOffset = offset;
            pixelsLength = width + (height - 1) * scanlength;
        } else
        {
            pixelsOffset = offset + (height - 1) * scanlength;
            pixelsLength = width + offset - pixelsOffset;
        }
        Array.checkBound("RasterBuffer.create", pixels.length, pixelsOffset, pixelsLength);
        return new RasterBuffer(pixels, offset, scanlength, width, height, opaque);
    }

    public static RasterBuffer create(RasterBuffer source, int left, int top, int width, int height) {
        int s;
        int lim;
        int srcWidth;
        int srcHeight;
        if(source == null)
        {
            throw new NullPointerException("RasterBuffer.create: аргумент source равен нулевой ссылке.");
        }
        if(width < 1 || width > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент width выходит из диапазона.");
        }
        if(height < 1 || height > MAX_SIZE)
        {
            throw new IllegalArgumentException("RasterBuffer.create: аргумент height выходит из диапазона.");
        }
        if((lim = left + width) > (srcWidth = source.width) || lim < left || left > srcWidth || left < 0)
        {
            throw new IllegalArgumentException("RasterBuffer.create: заданный регион выходит за пределы растрового буфера.");
        }
        if((lim = top + height) > (srcHeight = source.height) || lim < top || top > srcHeight || top < 0)
        {
            throw new IllegalArgumentException("RasterBuffer.create: заданный регион выходит за пределы растрового буфера.");
        }
        if(left == 0 && top == 0 && width == srcWidth && height == srcHeight)
        {
            return source;
        }
        return new RasterBuffer(source.pixels, source.offset + left + top * (s = source.scan), s, width, height, source.opaque);
    }

    public static RasterBuffer create(RasterBuffer source, Rectangle region) {
        if(region == null)
        {
            throw new NullPointerException("RasterBuffer.create: аргумент region равен нулевой ссылке.");
        }
        return create(source, region.getLeft(), region.getTop(), region.getWidth(), region.getHeight());
    }

    private final boolean opaque;
    private final int base;
    private final int hash;
    private final int scan;
    private final int size;
    private final int width;
    private final int height;
    private final int offset;
    private final int[] pixels;

    private RasterBuffer(int[] pixels, int offset, int scanlength, int width, int height, boolean opaque) {
        int base;
        this.opaque = opaque;
        this.base = base = Array.getFirstElementAddress(pixels) + (offset << 2);
        this.hash = base ^ (scanlength << 8) ^ width ^ (height << 16);
        this.scan = scanlength;
        this.size = width | height << 16;
        this.width = width;
        this.height = height;
        this.offset = offset;
        this.pixels = pixels;
    }

    public boolean equals(Object anot) {
        RasterBuffer b;
        return anot == this || anot instanceof RasterBuffer && opaque == (b = (RasterBuffer) anot).opaque && base == b.base && scan == b.scan && size == b.size;
    }

    public int hashCode() {
        return hash;
    }

    public String toString() {
        return (new StringBuilder()).
            append(opaque ? "Растровый буфер[непрозрачный, база=" : "Растровый буфер[полупрозрачный, база=").append(base, 16, false).
            append(", скан=").append(scan).append(", размер=").append(width).append("×").append(height).append("]").
        toString();
    }

    public void putPixel(int x, int y, int colorARGB) {
        if(x >= 0 && x < width && y >= 0 && y < height) pixels[offset + x + y * scan] = opaque ? colorARGB & 0x00ffffff : colorARGB;
    }

    public boolean isOpaque() {
        return opaque;
    }

    public int getPixel(int x, int y) {
        if(x < 0 || x >= width)
        {
            throw new IllegalArgumentException("RasterBuffer.getPixel: аргумент x выходит из диапазона.");
        }
        if(y < 0 || y >= height)
        {
            throw new IllegalArgumentException("RasterBuffer.getPixel: аргумент y выходит из диапазона.");
        }
        return pixels[offset + x + y * scan];
    }

    public int getBase() {
        return base;
    }

    public int getOffset() {
        return offset;
    }

    public int getScanlength() {
        return scan;
    }

    public int getSize() {
        return size;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public int[] getPixels() {
        return pixels;
    }
}
