/*
 * @(#)ILBMEncoder.java  1.0  2010-12-26
 * 
 * Copyright © 2010 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 * 
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package org.monte.media.ilbm;

import org.monte.media.image.BitmapImage;
import org.monte.media.iff.IFFOutputStream;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import javax.imageio.stream.FileImageOutputStream;

/**
 * {@code ILBMEncoder}.
 *
 * @author Werner Randelshofer
 * @version 1.0 2010-12-26 Created.
 */
public class ILBMEncoder {

    public ILBMEncoder() {
    }

    public void write(File f, BitmapImage img, int camg) throws IOException {
        IFFOutputStream out = null;
        try {
            out = new IFFOutputStream(new FileImageOutputStream(f));

            out.pushCompositeChunk("FORM", "ILBM");
            writeBMHD(out, img);
            writeCMAP(out, img);
            writeCAMG(out, camg);
            writeBODY(out, img);
            out.popChunk();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * Writes the bitmap header (ILBM BMHD).
     *
     * <pre>
     * typedef UBYTE Masking; // Choice of masking technique
     *
     * #define mskNone                 0
     * #define mskHasMask              1
     * #define mskHasTransparentColor  2
     * #define mskLasso                3
     *
     * typedef UBYTE Compression; // Choice of compression algorithm
     *     // applied to the rows of all source and mask planes.
     *     // "cmpByteRun1" is the byte run encoding. Do not compress
     *     // accross rows!
     * #define cmpNone      0
     * #define cmpByteRun1  1
     *
     * typedef struct {
     *   UWORD       w, h; // raster width & height in pixels
     *   WORD        x, y; // pixel position for this image
     *   UBYTE       nbPlanes; // # source bitplanes
     *   Masking     masking;
     *   Compression compression;
     *   UBYTE       pad1;     // unused; ignore on read, write as 0
     *   UWORD       transparentColor; // transparent "color number" (sort of)
     *   UBYTE       xAspect, yAspect; // pixel aspect, a ratio width : height
     *   UWORD       pageWidth, pageHeight; // source "page" size in pixels
     *   } BitmapHeader;
     * </pre>
     */
    private void writeBMHD(IFFOutputStream out, BitmapImage img) throws IOException {
        out.pushDataChunk("BMHD");
        out.writeUWORD(img.getWidth());
        out.writeUWORD(img.getHeight());
        out.writeWORD(0);
        out.writeWORD(0);
        out.writeUBYTE(img.getDepth());
        out.writeUBYTE(0); // mskNone
        out.writeUBYTE(1); // cmpByteRun1
        out.writeUBYTE(0);
        out.writeUWORD(0);
        out.writeUBYTE(44);
        out.writeUBYTE(52);
        out.writeUWORD(img.getWidth());
        out.writeUWORD(img.getHeight());
        out.popChunk();
    }

    /**
     * Writes the color map (ILBM CMAP).
     */
    private void writeCMAP(IFFOutputStream out, BitmapImage img) throws IOException {
        out.pushDataChunk("CMAP");

        IndexColorModel cm = (IndexColorModel) img.getPlanarColorModel();
        for (int i = 0, n = cm.getMapSize(); i < n; ++i) {
            out.writeUBYTE(cm.getRed(i));
            out.writeUBYTE(cm.getGreen(i));
            out.writeUBYTE(cm.getBlue(i));
        }

        out.popChunk();
    }

    /**
     * Writes the color amiga viewport mode display id (ILBM CAMG).
     */
    private void writeCAMG(IFFOutputStream out, int camg) throws IOException {
        out.pushDataChunk("CAMG");

        out.writeLONG(camg);

        out.popChunk();
    }

    /**
     * Writes the body (ILBM BODY).
     */
    private void writeBODY(IFFOutputStream out, BitmapImage img) throws IOException {
        out.pushDataChunk("BODY");

        int w = img.getWidth()/8;
        int ss=img.getScanlineStride();
        int bs=img.getBitplaneStride();

        int offset=0;

       byte[] data = img.getBitmap();

        for (int y = 0, h = img.getHeight(); y < h; y++) {
            for (int p = 0, d = img.getDepth(); p < d; p++) {
                out.writeByteRun1(data, offset+bs*p, w);
            }
            offset+=ss;
        }

        out.popChunk();
    }
}
