package com.jme3.scene.plugins.blender.textures.io;

import com.jme3.math.FastMath;
import com.jme3.scene.plugins.blender.textures.TexturePixel;
import com.jme3.texture.Image;
import java.nio.ByteBuffer;
import jme3tools.converters.RGB565;

/**
 * Implemens read/write operations for AWT images.
 * @author Marcin Roguski (Kaelthas)
 */
/* package */class AWTPixelInputOutput implements PixelInputOutput {
    public void read(Image image, int layer, TexturePixel pixel, int index) {
        ByteBuffer data = image.getData(layer);
        switch (image.getFormat()) {
            case RGBA8:
                pixel.fromARGB8(data.get(index + 3), data.get(index), data.get(index + 1), data.get(index + 2));
                break;
            case ARGB8:
                pixel.fromARGB8(data.get(index), data.get(index + 1), data.get(index + 2), data.get(index + 3));
                break;
            case ABGR8:
                pixel.fromARGB8(data.get(index), data.get(index + 3), data.get(index + 2), data.get(index + 1));
                break;
            case BGR8:
                pixel.fromARGB8((byte) 0xFF, data.get(index + 2), data.get(index + 1), data.get(index));
                break;
            case BGRA8:
                pixel.fromARGB8(data.get(index + 3), data.get(index + 2), data.get(index + 1), data.get(index));
                break;
            case RGB8:
                pixel.fromARGB8((byte) 0xFF, data.get(index), data.get(index + 1), data.get(index + 2));
                break;
            case RGB565:
                pixel.fromARGB8(RGB565.RGB565_to_ARGB8(data.getShort(index)));
                break;
            case RGB5A1:
                short rgb5a1 = data.getShort(index);
                byte a = (byte) (rgb5a1 & 0x01);
                int r = (rgb5a1 & 0xf800) >> 11 << 3;
                int g = (rgb5a1 & 0x07c0) >> 6 << 3;
                int b = (rgb5a1 & 0x001f) >> 1 << 3;
                pixel.fromARGB8(a == 1 ? (byte) 255 : 0, (byte) r, (byte) g, (byte) b);
                break;
            case RGB16F:
            case RGB16F_to_RGB111110F:
            case RGB16F_to_RGB9E5:
                pixel.fromARGB(1, FastMath.convertHalfToFloat(data.getShort(index)), FastMath.convertHalfToFloat(data.getShort(index + 2)), FastMath.convertHalfToFloat(data.getShort(index + 4)));
                break;
            case RGBA16F:
                pixel.fromARGB(FastMath.convertHalfToFloat(data.getShort(index + 6)), FastMath.convertHalfToFloat(data.getShort(index)), FastMath.convertHalfToFloat(data.getShort(index + 2)), FastMath.convertHalfToFloat(data.getShort(index + 4)));
                break;
            case RGBA32F:
                pixel.fromARGB(Float.intBitsToFloat(data.getInt(index + 12)), Float.intBitsToFloat(data.getInt(index)), Float.intBitsToFloat(data.getInt(index + 4)), Float.intBitsToFloat(data.getInt(index + 8)));
                break;
            case RGB111110F:// the data is stored as 32-bit unsigned int, that is why we cast the read data to long and remove MSB-bytes to get the positive value
                pixel.fromARGB(1, (float) Double.longBitsToDouble((long) data.getInt(index) & 0x00000000FFFFFFFF), (float) Double.longBitsToDouble((long) data.getInt(index + 4) & 0x00000000FFFFFFFF), (float) Double.longBitsToDouble((long) data.getInt(index + 8) & 0x00000000FFFFFFFF));
                break;
            case RGB9E5:// TODO: support these
                throw new IllegalStateException("Not supported image type for IO operations: " + image.getFormat());
            default:
                throw new IllegalStateException("Unknown image format: " + image.getFormat());
        }
    }

    public void read(Image image, int layer, TexturePixel pixel, int x, int y) {
        int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3);
        this.read(image, layer, pixel, index);
    }

    public void write(Image image, int layer, TexturePixel pixel, int index) {
        ByteBuffer data = image.getData(layer);
        switch (image.getFormat()) {
            case RGBA8:
                data.put(index, pixel.getR8());
                data.put(index + 1, pixel.getG8());
                data.put(index + 2, pixel.getB8());
                data.put(index + 3, pixel.getA8());
                break;
            case ARGB8:
                data.put(index, pixel.getA8());
                data.put(index + 1, pixel.getR8());
                data.put(index + 2, pixel.getG8());
                data.put(index + 3, pixel.getB8());
                break;
            case ABGR8:
                data.put(index, pixel.getA8());
                data.put(index + 1, pixel.getB8());
                data.put(index + 2, pixel.getG8());
                data.put(index + 3, pixel.getR8());
                break;
            case BGR8:
                data.put(index, pixel.getB8());
                data.put(index + 1, pixel.getG8());
                data.put(index + 2, pixel.getR8());
                break;
            case BGRA8:
                data.put(index, pixel.getB8());
                data.put(index + 1, pixel.getG8());
                data.put(index + 2, pixel.getR8());
                data.put(index + 3, pixel.getA8());
                break;
            case RGB8:
                data.put(index, pixel.getR8());
                data.put(index + 1, pixel.getG8());
                data.put(index + 2, pixel.getB8());
                break;
            case RGB565:
                data.putShort(RGB565.ARGB8_to_RGB565(pixel.toARGB8()));
                break;
            case RGB5A1:
                int argb8 = pixel.toARGB8();
                short r = (short) ((argb8 & 0x00F80000) >> 8);
                short g = (short) ((argb8 & 0x0000F800) >> 5);
                short b = (short) ((argb8 & 0x000000F8) >> 2);
                short a = (short) ((short) ((argb8 & 0xFF000000) >> 24) > 0 ? 1 : 0);
                data.putShort(index, (short) (r | g | b | a));
                break;
            case RGB16F:
            case RGB16F_to_RGB111110F:
            case RGB16F_to_RGB9E5:
                data.putShort(index, FastMath.convertFloatToHalf(pixel.red));
                data.putShort(index + 2, FastMath.convertFloatToHalf(pixel.green));
                data.putShort(index + 4, FastMath.convertFloatToHalf(pixel.blue));
                break;
            case RGBA16F:
                data.putShort(index, FastMath.convertFloatToHalf(pixel.red));
                data.putShort(index + 2, FastMath.convertFloatToHalf(pixel.green));
                data.putShort(index + 4, FastMath.convertFloatToHalf(pixel.blue));
                data.putShort(index + 6, FastMath.convertFloatToHalf(pixel.blue));
                break;
            case RGB32F:
            case RGB111110F:// this data is stored as 32-bit unsigned int
                data.putInt(index, Float.floatToIntBits(pixel.red));
                data.putInt(index + 2, Float.floatToIntBits(pixel.green));
                data.putInt(index + 4, Float.floatToIntBits(pixel.blue));
                break;
            case RGBA32F:
                data.putInt(index, Float.floatToIntBits(pixel.red));
                data.putInt(index + 2, Float.floatToIntBits(pixel.green));
                data.putInt(index + 4, Float.floatToIntBits(pixel.blue));
                data.putInt(index + 6, Float.floatToIntBits(pixel.alpha));
                break;
            case RGB9E5:// TODO: support these
                throw new IllegalStateException("Not supported image type for IO operations: " + image.getFormat());
            default:
                throw new IllegalStateException("Unknown image format: " + image.getFormat());
        }
    }

    public void write(Image image, int layer, TexturePixel pixel, int x, int y) {
        int index = (y * image.getWidth() + x) * (image.getFormat().getBitsPerPixel() >> 3);
        this.write(image, layer, pixel, index);
    }
}
