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

import com.jme3.math.FastMath;
import com.jme3.texture.Image.Format;

/**
 * The data that helps in bytes calculations for the result image.
 * 
 * @author Marcin Roguski (Kaelthas)
 */
/* package */class DDSTexelData {
    /** The colors of the texes. */
    private TexturePixel[][] colors;
    /** The indexes of the texels. */
    private long[]           indexes;
    /** The alphas of the texels (might be null). */
    private float[][]        alphas;
    /** The indexels of texels alpha values (might be null). */
    private long[]           alphaIndexes;
    /** The counter of texel x column. */
    private int              xCounter;
    /** The counter of texel y row. */
    private int              yCounter;
    /** The width of the image in pixels. */
    private int              widthInPixels;
    /** The height of the image in pixels. */
    private int              heightInPixels;
    /** The total texel count. */
    private int              xTexelCount;

    /**
     * Constructor. Allocates memory for data structures.
     * 
     * @param compressedSize
     *            the size of compressed image (or its mipmap)
     * @param widthToHeightRatio
     *            width/height ratio for the image
     * @param format
     *            the format of the image
     */
    public DDSTexelData(int compressedSize, float widthToHeightRatio, Format format) {
        int texelsCount = compressedSize * 8 / format.getBitsPerPixel() / 16;
        this.colors = new TexturePixel[texelsCount][];
        this.indexes = new long[texelsCount];
        this.widthInPixels = (int) (0.5f * (float) Math.sqrt(this.getSizeInBytes() / widthToHeightRatio));
        this.heightInPixels = (int) (this.widthInPixels / widthToHeightRatio);
        this.xTexelCount = widthInPixels >> 2;
        this.yCounter = (heightInPixels >> 2) - 1;// xCounter is 0 for now
        if (format == Format.DXT3 || format == Format.DXT5) {
            this.alphas = new float[texelsCount][];
            this.alphaIndexes = new long[texelsCount];
        }
    }

    /**
     * This method adds a color and indexes for a texel.
     * 
     * @param colors
     *            the colors of the texel
     * @param indexes
     *            the indexes of the texel
     */
    public void add(TexturePixel[] colors, int indexes) {
        this.add(colors, indexes, null, 0);
    }

    /**
     * This method adds a color, color indexes and alha values (with their
     * indexes) for a texel.
     * 
     * @param colors
     *            the colors of the texel
     * @param indexes
     *            the indexes of the texel
     * @param alphas
     *            the alpha values
     * @param alphaIndexes
     *            the indexes of the given alpha values
     */
    public void add(TexturePixel[] colors, int indexes, float[] alphas, long alphaIndexes) {
        int index = yCounter * xTexelCount + xCounter;
        this.colors[index] = colors;
        this.indexes[index] = indexes;
        if (alphas != null) {
            this.alphas[index] = alphas;
            this.alphaIndexes[index] = alphaIndexes;
        }
        ++this.xCounter;
        if (this.xCounter >= this.xTexelCount) {
            this.xCounter = 0;
            --this.yCounter;
        }
    }

    /**
     * This method returns the values of the pixel located on the given
     * coordinates on the result image.
     * 
     * @param x
     *            the x coordinate of the pixel
     * @param y
     *            the y coordinate of the pixel
     * @param result
     *            the table where the result is stored
     * @return <b>true</b> if the pixel was correctly read and <b>false</b> if
     *         the position was outside the image sizes
     */
    public boolean getRGBA8(int x, int y, byte[] result) {
        int xTexetlIndex = x % widthInPixels / 4;
        int yTexelIndex = y % heightInPixels / 4;

        int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex;
        if (texelIndex < colors.length) {
            TexturePixel[] colors = this.colors[texelIndex];

            // 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 = (int) (this.indexes[texelIndex] >> pixelIndexInTexel & indexMask);
            float alpha = this.alphas != null ? this.alphas[texelIndex][(int) (this.alphaIndexes[texelIndex] >> alphaIndexInTexel & 0x07)] : colors[colorIndex].alpha;
            result[0] = (byte) (colors[colorIndex].red * 255.0f);
            result[1] = (byte) (colors[colorIndex].green * 255.0f);
            result[2] = (byte) (colors[colorIndex].blue * 255.0f);
            result[3] = (byte) (alpha * 255.0f);
            return true;
        }
        return false;
    }

    /**
     * @return the size of the decompressed texel (in bytes)
     */
    public int getSizeInBytes() {
        // indexes.length == count of texels
        return indexes.length * 16 * 4;
    }

    /**
     * @return image (mipmap) width
     */
    public int getPixelWidth() {
        return widthInPixels;
    }

    /**
     * @return image (mipmap) height
     */
    public int getPixelHeight() {
        return heightInPixels;
    }
}
