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 DDS images.
 * This class currently implements only read operation.
 * @author Marcin Roguski (Kaelthas)
 */
/* package */class DDSPixelInputOutput implements PixelInputOutput {
    /**
     * For this class the index should be considered as a pixel index in AWT image format.
     */
    public void read(Image image, int layer, TexturePixel pixel, int index) {
        this.read(image, layer, pixel, index % image.getWidth(), index / image.getWidth());
    }

    public void read(Image image, int layer, TexturePixel pixel, int x, int y) {
        int xTexetlIndex = x % image.getWidth() >> 2;
        int yTexelIndex = y % image.getHeight() >> 2;
        int xTexelCount = image.getWidth() >> 2;
        int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;

        TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
        int indexes = 0;
        long alphaIndexes = 0;
        float[] alphas = null;
        ByteBuffer data = image.getData().get(layer);

        switch (image.getFormat()) {
            case DXT1: // BC1
            case DXT1A: {
                data.position(texelIndex * 8);
                short c0 = data.getShort();
                short c1 = data.getShort();
                int col0 = RGB565.RGB565_to_ARGB8(c0);
                int col1 = RGB565.RGB565_to_ARGB8(c1);
                colors[0].fromARGB8(col0);
                colors[1].fromARGB8(col1);

                if (col0 > col1) {
                    // creating color2 = 2/3color0 + 1/3color1
                    colors[2].fromPixel(colors[0]);
                    colors[2].mult(2);
                    colors[2].add(colors[1]);
                    colors[2].divide(3);

                    // creating color3 = 1/3color0 + 2/3color1;
                    colors[3].fromPixel(colors[1]);
                    colors[3].mult(2);
                    colors[3].add(colors[0]);
                    colors[3].divide(3);
                } else {
                    // creating color2 = 1/2color0 + 1/2color1
                    colors[2].fromPixel(colors[0]);
                    colors[2].add(colors[1]);
                    colors[2].mult(0.5f);

                    colors[3].fromARGB8(0);
                }
                indexes = data.getInt();// 4-byte table with color indexes in decompressed table
                break;
            }
            case DXT3: {// BC2
                data.position(texelIndex * 16);
                long alpha = data.getLong();
                alphas = new float[16];
                for (int i = 0; i < 16; ++i) {
                    alphaIndexes |= i << i * 4;
                    byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4);
                    alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
                }

                short c0 = data.getShort();
                short c1 = data.getShort();
                int col0 = RGB565.RGB565_to_ARGB8(c0);
                int col1 = RGB565.RGB565_to_ARGB8(c1);
                colors[0].fromARGB8(col0);
                colors[1].fromARGB8(col1);

                // creating color2 = 2/3color0 + 1/3color1
                colors[2].fromPixel(colors[0]);
                colors[2].mult(2);
                colors[2].add(colors[1]);
                colors[2].divide(3);

                // creating color3 = 1/3color0 + 2/3color1;
                colors[3].fromPixel(colors[1]);
                colors[3].mult(2);
                colors[3].add(colors[0]);
                colors[3].divide(3);

                indexes = data.getInt();// 4-byte table with color indexes in decompressed table
                break;
            }
            case DXT5: {// BC3
                data.position(texelIndex * 16);
                alphas = new float[8];
                alphas[0] = data.get() * 255.0f;
                alphas[1] = data.get() * 255.0f;
                // the casts to long must be done here because otherwise 32-bit integers would be shifetd by 32 and 40 bits which would result in improper values
                alphaIndexes = (long)data.get() | (long)data.get() << 8 | (long)data.get() << 16 | (long)data.get() << 24 | (long)data.get() << 32 | (long)data.get() << 40;
                if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
                    alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
                    alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
                    alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
                    alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
                    alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
                    alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
                } else {
                    alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
                    alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
                    alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
                    alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
                    alphas[6] = 0;
                    alphas[7] = 1;
                }

                short c0 = data.getShort();
                short c1 = data.getShort();
                int col0 = RGB565.RGB565_to_ARGB8(c0);
                int col1 = RGB565.RGB565_to_ARGB8(c1);
                colors[0].fromARGB8(col0);
                colors[1].fromARGB8(col1);

                // creating color2 = 2/3color0 + 1/3color1
                colors[2].fromPixel(colors[0]);
                colors[2].mult(2);
                colors[2].add(colors[1]);
                colors[2].divide(3);

                // creating color3 = 1/3color0 + 2/3color1;
                colors[3].fromPixel(colors[1]);
                colors[3].mult(2);
                colors[3].add(colors[0]);
                colors[3].divide(3);

                indexes = data.getInt();// 4-byte table with color indexes in decompressed table
                break;
            }
            default:
                throw new IllegalStateException("Unsupported decompression format.");
        }

        // coordinates of the pixel in the selected texel
        x = x - 4 * xTexetlIndex;// pixels are arranged from left to right
        y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start)

        int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2);
        int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0;

        // getting the pixel
        int indexMask = colors.length - 1;
        int colorIndex = indexes >> pixelIndexInTexel & indexMask;
        float alpha = alphas != null ? alphas[(int) (alphaIndexes >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha;
        pixel.fromPixel(colors[colorIndex]);
        pixel.alpha = alpha;
    }

    public void write(Image image, int layer, TexturePixel pixel, int index) {
        throw new UnsupportedOperationException("Cannot put the DXT pixel by index because not every index contains the pixel color!");
    }

    public void write(Image image, int layer, TexturePixel pixel, int x, int y) {
        throw new UnsupportedOperationException("Writing to DDS texture pixel by pixel is not yet supported!");
    }
}
