/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.image.types;

import java.awt.image.RenderedImage;
import java.io.IOException;

import com.lightcrafts.image.BadImageFileException;
import com.lightcrafts.image.ImageInfo;
import com.lightcrafts.image.metadata.*;
import com.lightcrafts.image.metadata.makernotes.NikonDirectory;
import com.lightcrafts.image.metadata.values.LongMetaValue;
import com.lightcrafts.image.UnknownImageTypeException;
import com.lightcrafts.utils.bytebuffer.LCByteBuffer;

import static com.lightcrafts.image.metadata.EXIFTags.EXIF_JPEG_INTERCHANGE_FORMAT;
import static com.lightcrafts.image.metadata.EXIFTags.EXIF_JPEG_INTERCHANGE_FORMAT_LENGTH;
import static com.lightcrafts.image.metadata.makernotes.NikonTags.*;

/**
 * A <code>NEFImageType</code> is-an {@link RawImageType} for NEF (Nikon raw
 * Format) images.
 *
 * @author Paul J. Lucas [paul@lightcrafts.com]
 */
public final class NEFImageType extends RawImageType implements TagHandler {

    ////////// public /////////////////////////////////////////////////////////

    /** The singleton instance of <code>NEFImageType</code>. */
    public static final NEFImageType INSTANCE = new NEFImageType();

    /**
     * {@inheritDoc}
     */
    public String[] getExtensions() {
        return EXTENSIONS;
    }

    /**
     * {@inheritDoc}
     */
    public String getName() {
        return "NEF";
    }

    /**
     * {@inheritDoc}
     */
    public RenderedImage getPreviewImage( ImageInfo imageInfo, int maxWidth,
                                          int maxHeight )
        throws BadImageFileException, IOException, UnknownImageTypeException
    {
        final RenderedImage image = TIFFImageType.getJPEGInterchangeImage(
            imageInfo, maxWidth, maxHeight
        );
        return  image != null &&
                // The following is a hack to support NRW files
                image.getWidth() > 300 &&
                image.getHeight() > 300 ?
                image : super.getPreviewImage( imageInfo, maxWidth, maxHeight );
    }

    public boolean hasFastPreview() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public RenderedImage getThumbnailImage( ImageInfo imageInfo )
        throws BadImageFileException, IOException, UnknownImageTypeException
    {
        final ImageMetadata metadata = imageInfo.getMetadata();
        final ImageMetadataDirectory dir =
            metadata.getDirectoryFor( NikonDirectory.class );
        if ( dir != null ) {
            // TODO: verify that this is actually an sRGB image, what about Adobe RGB shooting, etc.?
            return JPEGImageType.getImageFromBuffer(
                imageInfo.getByteBuffer(),
                dir.getValue( NIKON_PREVIEW_IMAGE_OFFSET ), 0,
                dir.getValue( NIKON_PREVIEW_IMAGE_LENGTH ),
                0, 0
            );
        }
        return super.getThumbnailImage( imageInfo );
    }

    /**
     * {@inheritDoc}
     */
    public boolean handleTag( int tagID, int fieldType, int numValues,
                              int byteCount, int valueOffset,
                              int valueOffsetAdjustment, int subdirOffset,
                              ImageInfo imageInfo, LCByteBuffer buf,
                              ImageMetadataDirectory dir )
        throws IOException
    {
        switch ( tagID ) {
            case NIKON_PREVIEW_IMAGE_IFD_POINTER: {
                //
                // The NikonPreviewImage tag's value is an offset to a subEXIF
                // directory containing 7 metadata tags/values for the preview
                // image.  The 7 (in order) are: Compression, XResolution,
                // YResolution, ResolutionUnit, JPEGInterchangeFormat,
                // JPEGInterchangeFormatLength, and YCbCrPositioning.  Of
                // those, we only care about JPEGInterchangeFormat and
                // JPEGInterchangeFormatLength, i.e., the starting offset and
                // length of the preview JPEG image.
                //
                final EXIFMetadataReader reader = new EXIFMetadataReader(
                    imageInfo, buf, true
                );
                //
                // Create temporary ImageMetadata and EXIFDirectory objects to
                // read the metadata into because we don't want to overwrite
                // the values of the tags we're not interested in in the full-
                // sized image (if they exist).
                //
                final ImageMetadata tempMetadata = new ImageMetadata();
                final ImageMetadataDirectory tempDir =
                    tempMetadata.getDirectoryFor( EXIFDirectory.class, true );
                reader.readDirectory( subdirOffset, 0, tempDir );
                //
                // Now pluck out the tags' values we're interested in and
                // insert them (and only them) into the Nikon directory.
                //
                final LongMetaValue offsetValue =
                    (LongMetaValue)tempDir.getValue(
                        EXIF_JPEG_INTERCHANGE_FORMAT
                    );
                final LongMetaValue lengthValue =
                    (LongMetaValue)tempDir.getValue(
                        EXIF_JPEG_INTERCHANGE_FORMAT_LENGTH
                    );
                if ( offsetValue == null || lengthValue == null )
                    return true;
                final int offset = offsetValue.getIntValue();
                final int length = lengthValue.getIntValue();
                if ( offset <= 0 || length <= 0 )
                    return true;
                final ImageMetadataDirectory nikonDir =
                    imageInfo.getCurrentMetadata().getDirectoryFor(
                        NikonDirectory.class, true
                    );
                offsetValue.setLongValueAt( offset + valueOffsetAdjustment, 0 );
                nikonDir.putValue( NIKON_PREVIEW_IMAGE_OFFSET, offsetValue );
                nikonDir.putValue( NIKON_PREVIEW_IMAGE_LENGTH, lengthValue );
                return true;
            }
            default:
                return false;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void readMetadata( ImageInfo imageInfo )
        throws BadImageFileException, IOException
    {
        final TIFFMetadataReader reader = new TIFFMetadataReader( imageInfo );
        reader.setTagHandler( this );
        MetadataUtil.removePreviewMetadataFrom( reader.readMetadata() );
    }

    ////////// private ////////////////////////////////////////////////////////

    /**
     * Construct a <code>NEFImageType</code>.
     * The constructor is <code>private</code> so only the singleton instance
     * can be constructed.
     */
    private NEFImageType() {
        // do nothing
    }

    /**
     * All the possible filename extensions for NEF files.  All must be lower
     * case and the preferred one must be first.
     */
    private static final String EXTENSIONS[] = {
        "nef", "nrw"
    };
}
/* vim:set et sw=4 ts=4: */
