/*
    Реализация спецификаций 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 javax.microedition.lcdui;

import java.io.*;
import javax.microedition.lcdui.game.*;
import malik.emulator.fileformats.graphics.*;
import malik.emulator.io.jar.*;
import malik.emulator.media.graphics.*;
import malik.emulator.microedition.*;
import malik.emulator.util.*;

public class Image extends Object
{
    public static Image createImage(int width, int height) {
        int area;
        int[] pixels;
        if(width <= 0)
        {
            throw new IllegalArgumentException("Image.createImage: аргумент width может быть только положительным.");
        }
        if(height <= 0)
        {
            throw new IllegalArgumentException("Image.createImage: аргумент height может быть только положительным.");
        }
        Array.fill(pixels = new int[area = width * height], 0, area, 0xffffffff);
        return new Image(pixels, width, height, true);
    }

    public static Image createImage(byte[] src, int offset, int length) {
        int width;
        int height;
        int[] pixels;
        ImageDecoder decoder;
        if(src == null)
        {
            throw new NullPointerException("Image.createImage: аргумент src равен нулевой ссылке.");
        }
        Array.checkBound("Image.createImage", src.length, offset, length);
        try
        {
            InputStream stream = new ByteArrayInputStream(src, offset, length);
            (decoder = DeviceManager.getInstance().openImageDecoder(stream)).loadFromInputStream(stream);
        }
        catch(Exception e)
        {
            throw new IllegalArgumentException("Image.createImage: не удалось декодировать изображение.");
        }
        width = decoder.getWidth();
        height = decoder.getHeight();
        pixels = decoder.getPixels();
        if(!decoder.alphaSupported()) for(int i = width * height; i-- > 0; pixels[i] |= 0xff000000);
        return new Image(pixels, width, height, false);
    }

    public static Image createImage(InputStream stream) throws IOException {
        int width;
        int height;
        int[] pixels;
        ImageDecoder decoder;
        if(stream == null)
        {
            throw new NullPointerException("Image.createImage: аргумент stream равен нулевой ссылке.");
        }
        (decoder = DeviceManager.getInstance().openImageDecoder(stream)).loadFromInputStream(stream);
        width = decoder.getWidth();
        height = decoder.getHeight();
        pixels = decoder.getPixels();
        if(!decoder.alphaSupported()) for(int i = width * height; i-- > 0; pixels[i] |= 0xff000000);
        return new Image(pixels, width, height, false);
    }

    public static Image createImage(String resourceName) throws IOException {
        int width;
        int height;
        int[] pixels;
        ImageDecoder decoder;
        InputStream stream;
        if(resourceName == null)
        {
            throw new NullPointerException("Image.createImage: аргумент resourceName равен нулевой ссылке.");
        }
        if((stream = resourceName.getClass().getResourceAsStream(resourceName.length() > 0 && resourceName.charAt(0) == '/' ? resourceName : "/".concat(resourceName))) == null)
        {
            throw new ResourceNotFoundException((new StringBuilder()).append("Image.createImage: ресурс ").append(resourceName).append(" не найден.").toString());
        }
        (decoder = DeviceManager.getInstance().openImageDecoder(stream)).loadFromInputStream(stream);
        width = decoder.getWidth();
        height = decoder.getHeight();
        pixels = decoder.getPixels();
        if(!decoder.alphaSupported()) for(int i = width * height; i-- > 0; pixels[i] |= 0xff000000);
        return new Image(pixels, width, height, false);
    }

    public static Image createImage(Image source) {
        int area;
        int width;
        int height;
        int[] pixels;
        if(source == null)
        {
            throw new NullPointerException("Image.createImage: аргумент source равен нулевой ссылке.");
        }
        if(!source.mutable) return source;
        Array.copy(source.pixels, 0, pixels = new int[area = (width = source.width) * (height = source.height)], 0, area);
        return new Image(pixels, width, height, false);
    }

    public static Image createImage(Image source, int left, int top, int width, int height, int transform) {
        int lim;
        int area;
        int xinc;
        int yinc;
        int start;
        int srcWidth;
        int srcHeight;
        int[] srcPixels;
        int[] dstPixels;
        if(source == null)
        {
            throw new NullPointerException("Image.createImage: аргумент source равен нулевой ссылке.");
        }
        srcWidth = source.width;
        srcHeight = source.height;
        if(!source.mutable && (left | top) == 0 && width == srcWidth && height == srcHeight && transform == Sprite.TRANS_NONE) return source;
        if(width <= 0)
        {
            throw new IllegalArgumentException("Image.createImage: аргумент width может быть только положительным.");
        }
        if(height <= 0)
        {
            throw new IllegalArgumentException("Image.createImage: аргумент height может быть только положительным.");
        }
        if((lim = left + width) > srcWidth || lim < left || left > srcWidth || left < 0 || (lim = top + height) > srcHeight || lim < top || top > srcHeight || top < 0)
        {
            throw new IllegalArgumentException("Image.createImage: заданный регион выходит за пределы изображения.");
        }
        if((transform & (-8)) != 0)
        {
            throw new IllegalArgumentException("Image.createImage: аргумент transform имеет недопустимое значение.");
        }
        srcPixels = source.pixels;
        dstPixels = new int[area = width * height];
        for(int i = left + top * srcWidth, j = 0, cy = height; cy-- > 0; i += srcWidth, j += width) Array.copy(srcPixels, i, dstPixels, j, width);
        srcPixels = dstPixels;
        dstPixels = new int[area];
        if((transform & 1) != 0)
        {
            yinc = -width;
            start = area - width;
        } else
        {
            yinc = width;
            start = 0;
        }
        if((transform & 2) != 0)
        {
            xinc = -1;
            start += width - 1;
        } else
        {
            xinc = 1;
        }
        if((transform & 4) != 0)
        {
            for(int i = 0, cx = width; cx-- > 0; start += xinc) for(int j = start, cy = height; cy-- > 0; j += yinc)
            {
                dstPixels[i++] = srcPixels[j];
            }
            return new Image(dstPixels, height, width, false);
        }
        for(int i = 0, cy = height; cy-- > 0; start += yinc) for(int j = start, cx = width; cx-- > 0; j += xinc) dstPixels[i++] = srcPixels[j];
        return new Image(dstPixels, width, height, false);
    }

    public static Image createRGBImage(int[] pixels, int width, int height, boolean alpha) {
        return createRGBImage(pixels, width, height, alpha, false);
    }

    public static Image createRGBImage(int[] pixels, int width, int height, boolean alpha, boolean mutable) {
        int area;
        if(pixels == null)
        {
            throw new NullPointerException("Image.createRGBImage: аргумент pixels равен нулевой ссылке.");
        }
        if(width <= 0)
        {
            throw new IllegalArgumentException("Image.createRGBImage: аргумент width может быть только положительным.");
        }
        if(height <= 0)
        {
            throw new IllegalArgumentException("Image.createRGBImage: аргумент height может быть только положительным.");
        }
        if((long) pixels.length < (long) width * (long) height)
        {
            throw new ArrayIndexOutOfBoundsException("Image.createRGBImage: длина массива pixels меньше заданной площади изображения.");
        }
        Array.copy(pixels, 0, pixels = new int[area = width * height], 0, area);
        if(!alpha) for(int i = area; i-- > 0; pixels[i] |= 0xff000000);
        return new Image(pixels, width, height, mutable);
    }

    private final boolean mutable;
    final int width;
    final int height;
    private final int[] pixels;
    final RasterBuffer buffer;

    private Image(int[] pixels, int width, int height, boolean mutable) {
        this.mutable = mutable;
        this.width = width;
        this.height = height;
        this.pixels = pixels;
        this.buffer = RasterBuffer.create(pixels, 0, width, width, height, false);
    }

    public void getRGB(int[] pixels, int offset, int scanlength, int left, int top, int width, int height) {
        int lim;
        int len;
        int pixelsOffset;
        int pixelsLength;
        int srcWidth = this.width;
        int[] srcPixels = this.pixels;
        if(scanlength >= 0)
        {
            pixelsOffset = offset;
            pixelsLength = width + (height - 1) * scanlength;
        } else
        {
            pixelsOffset = offset + (height - 1) * scanlength;
            pixelsLength = width + offset - pixelsOffset;
        }
        if((lim = left + width) > (len = srcWidth) || lim < left || left > len || left < 0 || (lim = top + height) > (len = this.height) || lim < top || top > len || top < 0)
        {
            throw new IllegalArgumentException("Image.getRGB: заданный регион выходит за пределы изображения.");
        }
        if(scanlength > -width && scanlength < width)
        {
            throw new IllegalArgumentException("Image.getRGB: аргумент scanlength выходит из диапазона.");
        }
        if(pixels == null)
        {
            throw new NullPointerException("Image.getRGB: аргумент pixels равен нулевой ссылке.");
        }
        Array.checkBound("Image.getRGB", pixels.length, pixelsOffset, pixelsLength);
        for(int i = left + top * srcWidth, j = offset, cy = height; cy-- > 0; i += srcWidth, j += scanlength) Array.copy(srcPixels, i, pixels, j, width);
    }

    public boolean isMutable() {
        return mutable;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public Graphics getGraphics() {
        if(!mutable)
        {
            throw new IllegalStateException("Image.getGraphics: неизменяемое изображение.");
        }
        return new Graphics(buffer);
    }
}
