// Copyright 2001, FreeHEP.
package org.freehep.graphicsio.emf;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.IOException;

/**
 * EMF File Header.
 *
 * @author Mark Donszelmann
 * @version $Id: EMFHeader.java,v 1.1.1.1 2004/05/31 05:08:53 kuromaru Exp $
 */
public class EMFHeader implements EMFConstants {
    private static final Dimension screenMM = new Dimension( 320, 240 );
    private Rectangle bounds;
    private Rectangle frame;
    private String signature;
    private int versionMajor;
    private int versionMinor;
    private int bytes;
    private int records;
    private int handles;
    private String description;
    private int palEntries;
    private Dimension device;
    private Dimension millimeters;
    private Dimension micrometers;
    private boolean openGL;

    public EMFHeader( Rectangle bounds, int versionMajor, int versionMinor, int bytes, int records,
        int handles, String application, String name, Dimension device ) {
        this.bounds = bounds;

        // this assumes you use MM_ANISOTROPIC or MM_ISOTROPIC as MapMode
        double pixelWidth = ( double ) screenMM.width / device.width;
        double pixelHeight = ( double ) screenMM.height / device.height;
        this.frame = new Rectangle( ( int ) ( bounds.x * 100 * pixelWidth ),
                ( int ) ( bounds.y * 100 * pixelHeight ),
                ( int ) ( bounds.width * 100 * pixelWidth ),
                ( int ) ( bounds.height * 100 * pixelHeight ) );

        this.signature = " EMF";
        this.versionMajor = versionMajor;
        this.versionMinor = versionMinor;
        this.bytes = bytes;
        this.records = records;
        this.handles = handles;
        this.description = application.trim(  ) + "\0" + name.trim(  ) + "\0\0";

        this.palEntries = 0;
        this.device = device;
        this.millimeters = screenMM;

        this.openGL = false;
        this.micrometers = new Dimension( screenMM.width * 1000, screenMM.height * 1000 );
    }

    EMFHeader( EMFInputStream emf ) throws IOException {
        // FIXME: incomplete
        emf.readUnsignedInt(  ); // 4

        int length = emf.readDWORD(  ); // 8

        bounds = emf.readRECTL(  ); // 24
        frame = emf.readRECTL(  ); // 40
        signature = new String( emf.readBYTE( 4 ) ); // 44

        int version = emf.readDWORD(  ); // 48
        versionMajor = version >> 16;
        versionMinor = version & 0xFFFF;
        bytes = emf.readDWORD(  ); // 52
        records = emf.readDWORD(  ); // 56
        handles = emf.readWORD(  ); // 58
        emf.readWORD(  ); // 60

        int dLen = emf.readDWORD(  ); // 64
        int dOffset = emf.readDWORD(  ); // 68
        palEntries = emf.readDWORD(  ); // 72
        device = emf.readSIZEL(  ); // 80
        millimeters = emf.readSIZEL(  ); // 88
        if ( ( length - ( 2 * dLen ) ) > 88 ) {
            emf.readDWORD(  ); // 92
            emf.readDWORD(  ); // 96
            openGL = ( emf.readDWORD(  ) != 0 ) ? true : false; // 100
            if ( ( length - ( 2 * dLen ) ) > 100 ) {
                micrometers = emf.readSIZEL(  ); // 108
            }
        }

        // FIXME: dOffset ignored
        description = emf.readWCHAR( dLen );

        // the rest...
        if ( ( length - ( 2 * dLen ) ) > 108 ) {
            emf.readUnsignedByte( length - ( 2 * dLen ) - 108 );
        }
    }

    public void write( EMFOutputStream emf ) throws IOException {
        int align = emf.getTagAlignment(  );
        int padding = ( align - ( size(  ) % align ) ) % align;
        int alignedSize = size(  ) + padding;

        emf.writeDWORD( 0x00000001 ); // Header Type
        emf.writeDWORD( alignedSize ); // length of header
        emf.writeRECTL( bounds ); // inclusive bounds
        emf.writeRECTL( frame ); // inclusive picture

        emf.writeBYTE( signature.getBytes(  ) ); // signature ID EMF
        emf.writeDWORD( ( versionMajor << 16 ) | versionMinor ); // version
        emf.writeDWORD( alignedSize + bytes ); // file size
        emf.writeDWORD( records ); // # of records
        emf.writeWORD( handles ); // # of handles, 1 minimum
        emf.writeWORD( 0 ); // reserved
        emf.writeDWORD( description.length(  ) ); // size of descriptor in WORDS
        emf.writeDWORD( 0x6C ); // offset to descriptor
        emf.writeDWORD( palEntries ); // # of palette entries
        emf.writeSIZEL( device ); // size of ref device
        emf.writeSIZEL( millimeters ); // size of ref device in MM
        emf.writeDWORD( 0 ); // cbPixelFormat
        emf.writeDWORD( 0 ); // offPixelFormat
        emf.writeDWORD( openGL ); // bOpenGL
        emf.writeSIZEL( micrometers ); // size of ref device in microns

        // optional description
        emf.writeWCHAR( description );

        // padding
        for ( int i = 0; i < padding; i++ ) {
            emf.write( 0 );
        }
    }

    /** size of emf file in bytes ? */
    public int size(  ) {
        return 108 + ( 2 * description.length(  ) );
    }

    public String toString(  ) {
        StringBuffer s = new StringBuffer( "EMF Header\n" );
        s.append( "  bounds: " + bounds + "\n" );
        s.append( "  frame: " + frame + "\n" );
        s.append( "  signature: " + signature + "\n" );
        s.append( "  versionMajor: " + versionMajor + "\n" );
        s.append( "  versionMinor: " + versionMinor + "\n" );
        s.append( "  #bytes: " + bytes + "\n" );
        s.append( "  #records: " + records + "\n" );
        s.append( "  #handles: " + handles + "\n" );
        s.append( "  description: " + description + "\n" );
        s.append( "  #palEntries: " + palEntries + "\n" );
        s.append( "  device: " + device + "\n" );
        s.append( "  millimeters: " + millimeters + "\n" );

        s.append( "  openGL: " + openGL + "\n" );
        s.append( "  micrometers: " + micrometers );

        return s.toString(  );
    }

    /** Specifies the dimensions, in device units, of the smallest rectangle that can be drawn around
     * the picture stored in the metafile. This rectangle is supplied by graphics device interface (GDI).
     * Its dimensions include the right and bottom edges.
     **/
    public Rectangle getBounds(  ) {
        return bounds;
    }

    /**Specifies the dimensions, in .01 millimeter units, of a rectangle that surrounds the picture stored
     * in the metafile. This rectangle must be supplied by the application that creates the metafile. 
     * Its dimensions include the right and bottom edges.
     */
    public Rectangle getFrame(  ) {
        return frame;
    }

    /**Specifies a double word signature. This member must specify the value assigned 
     * to the ENHMETA_SIGNATURE constant.
     */
    public String getSignature(  ) {
        return signature;
    }

    /**  the description of the enhanced metafile's contents */
    public String getDescription(  ) {
        return description;
    }

    /** Specifies the resolution of the reference device, in pixels. */
    public Dimension getDevice(  ) {
        return device;
    }

    /**Specifies the resolution of the reference device, in millimeters. */
    public Dimension getMillimeters(  ) {
        return millimeters;
    }

    /** Windows 98/Me, Windows 2000/XP: Size of the reference device in micrometers. */
    public Dimension getMicrometers(  ) {
        return micrometers;
    }

    /** Windows 95/98/Me, Windows NT 4.0 and later: Specifies whether any OpenGL records 
     * are present in a metafile. bOpenGL is a simple Boolean flag that you can use to determine
     * whether an enhanced metafile requires OpenGL handling. When a metafile contains 
     * OpenGL records, bOpenGL is TRUE; otherwise it is FALSE.
     */
    public boolean isOpenGL(  ) {
        return openGL;
    }
}
