package org.sunflow.image.readers;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.sunflow.image.Bitmap;
import org.sunflow.image.BitmapReader;
import org.sunflow.image.formats.BitmapXYZ;

/**
 * Reads images in Indigo's native XYZ format.
 * http://www2.indigorenderer.com/joomla/forum/viewtopic.php?p=11430
 */
public class IGIBitmapReader implements BitmapReader {
    public Bitmap load(String filename, boolean isLinear) throws IOException, BitmapFormatException {
        InputStream stream = new BufferedInputStream(new FileInputStream(filename));
        // read header
        int magic = read32i(stream);
        int version = read32i(stream);
        stream.skip(8); // skip number of samples (double value)
        int width = read32i(stream);
        int height = read32i(stream);
        int superSample = read32i(stream); // super sample factor
        int compression = read32i(stream);
        int dataSize = read32i(stream);
        int colorSpace = read32i(stream);
        stream.skip(5000); // skip the rest of the header (unused for now)
        // error checking
        if (magic != 66613373)
            throw new BitmapFormatException("wrong magic: " + magic);
        if (version != 1)
            throw new BitmapFormatException("unsupported version: " + version);
        if (compression != 0)
            throw new BitmapFormatException("unsupported compression: " + compression);
        if (colorSpace != 0)
            throw new BitmapFormatException("unsupported color space: " + colorSpace);
        if (dataSize != (width * height * 12))
            throw new BitmapFormatException("invalid data block size: " + dataSize);
        if (width <= 0 || height <= 0)
            throw new BitmapFormatException("invalid image size: " + width + "x" + height);
        if (superSample <= 0)
            throw new BitmapFormatException("invalid super sample factor: " + superSample);
        if ((width % superSample) != 0 || (height % superSample) != 0)
            throw new BitmapFormatException("invalid image size: " + width + "x" + height);
        float[] xyz = new float[width * height * 3];
        for (int y = 0, i = 3 * (height - 1) * width; y < height; y++, i -= 6 * width) {
            for (int x = 0; x < width; x++, i += 3) {
                xyz[i + 0] = read32f(stream);
                xyz[i + 1] = read32f(stream);
                xyz[i + 2] = read32f(stream);
            }
        }
        stream.close();
        if (superSample > 1) {
            // rescale image (basic box filtering)
            float[] rescaled = new float[xyz.length / (superSample * superSample)];
            float inv = 1.0f / (superSample * superSample);
            for (int y = 0, i = 0; y < height; y += superSample) {
                for (int x = 0; x < width; x += superSample, i += 3) {
                    float X = 0;
                    float Y = 0;
                    float Z = 0;
                    for (int sy = 0; sy < superSample; sy++) {
                        for (int sx = 0; sx < superSample; sx++) {
                            int offset = 3 * ((x + sx + (y + sy) * width));
                            X += xyz[offset + 0];
                            Y += xyz[offset + 1];
                            Z += xyz[offset + 2];
                        }
                    }
                    rescaled[i + 0] = X * inv;
                    rescaled[i + 1] = Y * inv;
                    rescaled[i + 2] = Z * inv;
                }
            }
            return new BitmapXYZ(width / superSample, height / superSample, rescaled);
        } else
            return new BitmapXYZ(width, height, xyz);
    }

    private static final int read32i(InputStream stream) throws IOException {
        int i = stream.read();
        i |= stream.read() << 8;
        i |= stream.read() << 16;
        i |= stream.read() << 24;
        return i;
    }

    private static final float read32f(InputStream stream) throws IOException {
        return Float.intBitsToFloat(read32i(stream));
    }
}