/*
 * Copyright 1999-2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */
 
package org.apache.fop.image;

// Java
import java.io.IOException;
import java.awt.color.ColorSpace;

// FOP
import org.apache.avalon.framework.logger.Logger;

/**
 * Bitmap image.
 * This supports loading a bitmap image into bitmap data.
 *
 * @author Art WELCH
 * @see AbstractFopImage
 * @see FopImage
 */
public class BmpImage extends AbstractFopImage {
    /**
     * Create a bitmap image with the image data.
     *
     * @param imgInfo the image information
     */
    public BmpImage(FopImage.ImageInfo imgInfo) {
        super(imgInfo);
    }

    /**
     * Load the bitmap.
     * This laods the bitmap data from the bitmap image.
     *
     * @param ua the user agent
     * @return true if it was loaded successfully
     */
    protected boolean loadBitmap(Logger logger) {
        int wpos = 18;
        int hpos = 22; // offset positioning for w and height in  bmp files
        int[] headermap = new int[54];
        int filepos = 0;
        byte palette[] = null;
        try {
            boolean eof = false;
            while ((!eof) && (filepos < 54)) {
                int input = inputStream.read();
                if (input == -1) {
                    eof = true;
                } else {
                    headermap[filepos++] = input;
                }
            }

            if (headermap[28] == 4 || headermap[28] == 8) {
                int palettesize = 1 << headermap[28];
                palette = new byte[palettesize * 3];
                int countr = 0;
                while (!eof && countr < palettesize) {
                    int count2 = 2;
                    while (!eof && count2 >= -1) {
                        int input = inputStream.read();
                        if (input == -1) {
                            eof = true;
                        } else if (count2 >= 0) {
                            palette[countr * 3 + count2] =
                              (byte)(input & 0xFF);
                        }
                        count2--;
                        filepos++;
                    }
                    countr++;
                }
            }
        } catch (IOException e) {
            logger.error("Error while loading image "
                                         + "" + " : "
                                         + e.getClass() + " - "
                                         + e.getMessage(), e);
            return false;
        }
        // gets h & w from headermap
        this.width = headermap[wpos] 
                + headermap[wpos + 1] * 256
                + headermap[wpos + 2] * 256 * 256
                + headermap[wpos + 3] * 256 * 256 * 256;
        this.height = headermap[hpos] 
                + headermap[hpos + 1] * 256
                + headermap[hpos + 2] * 256 * 256
                + headermap[hpos + 3] * 256 * 256 * 256;

        int imagestart = headermap[10] 
                + headermap[11] * 256
                + headermap[12] * 256 * 256
                + headermap[13] * 256 * 256 * 256;
        this.bitsPerPixel = headermap[28];
        this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
        int bytes = 0;
        if (this.bitsPerPixel == 1) {
            bytes = (this.width + 7) / 8;
        } else if (this.bitsPerPixel == 24) {
            bytes = this.width * 3;
        } else if (this.bitsPerPixel == 4 || this.bitsPerPixel == 8) {
            bytes = this.width / (8 / this.bitsPerPixel);
        } else {
            logger.error("Image (" + ""
                          + ") has " + this.bitsPerPixel
                          + " which is not a supported BMP format.");
            return false;
        }
        if ((bytes & 0x03) != 0) {
            bytes |= 0x03;
            bytes++;
        }

        // Should take care of the ColorSpace and bitsPerPixel
        this.bitmapsSize = this.width * this.height * 3;
        this.bitmaps = new byte[this.bitmapsSize];

        int[] temp = new int[bytes * this.height];
        try {
            int input;
            int count = 0;
            inputStream.skip((long)(imagestart - filepos));
            while ((input = inputStream.read()) != -1) {
                temp[count++] = input;
            }
            inputStream.close();
            inputStream = null;
        } catch (IOException e) {
            logger.error("Error while loading image "
                          + "" + " : "
                          + e.getClass() + " - "
                          + e.getMessage(), e);
            return false;
        }

        for (int i = 0; i < this.height; i++) {
            int x = 0;
            int j = 0;
            while (j < bytes) {
                int p = temp[(this.height - i - 1) * bytes + j];

                if (this.bitsPerPixel == 24 && x < this.width) {
                    int countr = 2;
                    do {
                        this.bitmaps[3 * (i * this.width + x) + countr] =
                                         (byte)(temp[(this.height - i - 1)
                                                     * bytes + j] & 0xFF);
                        j++;
                    } while (--countr >= 0)
                        ;
                    x++;
                } else if (this.bitsPerPixel == 1) {
                    for (int countr = 0;
                            countr < 8 && x < this.width; countr++) {
                        if ((p & 0x80) != 0) {
                            this.bitmaps[3 * (i * this.width + x)] = (byte) 0xFF;
                            this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0xFF;
                            this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0xFF;
                        } else {
                            this.bitmaps[3 * (i * this.width + x)] = (byte) 0;
                            this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0;
                            this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0;
                        }
                        p <<= 1;
                        x++;
                    }
                    j++;
                } else if (this.bitsPerPixel == 4) {
                    for (int countr = 0;
                            countr < 2 && x < this.width; countr++) {
                        int pal = ((p & 0xF0) >> 4) * 3;
                        this.bitmaps[3 * (i * this.width + x)] = palette[pal];
                        this.bitmaps[3 * (i * this.width + x) + 1] = palette[pal + 1];
                        this.bitmaps[3 * (i * this.width + x) + 2] = palette[pal + 2];
                        p <<= 4;
                        x++;
                    }
                    j++;
                } else if (this.bitsPerPixel == 8) {
                    if (x < this.width) {
                        p *= 3;
                        this.bitmaps[3 * (i * this.width + x)] = palette[p];
                        this.bitmaps[3 * (i * this.width + x) + 1] = palette[p + 1];
                        this.bitmaps[3 * (i * this.width + x) + 2] = palette[p + 2];
                        j++;
                        x++;
                    } else {
                        j = bytes;
                    }
                } else {
                    j++;
                }
            }
        }

        // This seems really strange to me, but I noticed that
        // JimiImage hardcodes bitsPerPixel to 8. If I do not
        // do this Acrobat is unable to read the resultant PDF,
        // so we will hardcode this...
        this.bitsPerPixel = 8;

        return true;
    }

}

