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

package com.lightcrafts.image.metadata;

import java.util.HashMap;
import java.util.Map;
import java.util.Date;
import java.util.ResourceBundle;

import com.lightcrafts.image.metadata.values.DateMetaValue;
import com.lightcrafts.image.metadata.values.ImageMetaValue;
import com.lightcrafts.image.metadata.values.UnsignedShortMetaValue;
import com.lightcrafts.image.metadata.values.UnsignedLongMetaValue;
import com.lightcrafts.image.metadata.providers.*;
import com.lightcrafts.utils.Rational;
import com.lightcrafts.utils.TextUtil;

import static com.lightcrafts.image.metadata.CIFFTags.*;
import static com.lightcrafts.image.metadata.ImageMetaType.*;
import static com.lightcrafts.image.metadata.ImageOrientation.*;
import static com.lightcrafts.image.types.CIFFConstants.*;

/**
 * A <code>CIFFDirectory</code> is-an {@link ImageMetadataDirectory} for
 * holding CIFF metadata.
 *
 * @author Paul J. Lucas [paul@lightcrafts.com]
 * @see <i>CIFF 1.0r4</i>, Canon Incorporated, December 1997.
 */
@SuppressWarnings({"CloneableClassWithoutClone"})
public final class CIFFDirectory extends ImageMetadataDirectory implements
    ApertureProvider, ArtistProvider, BitsPerChannelProvider,
    CaptureDateTimeProvider, ColorTemperatureProvider, FocalLengthProvider,
    ISOProvider, LensProvider, MakeModelProvider, OrientationProvider,
    ShutterSpeedProvider, WidthHeightProvider {

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

    /**
     * Convert the metadata values in this <code>CIFFDirectory</code> to those
     * in a {@link TIFFDirectory} and/or an {@link EXIFDirectory}.
     *
     * @param forJPEG Should be <code>true</code> if the converted metadata is
     * for a JPEG file; should be <code>false</code> for a TIFF file.
     * @return Returns a new {@link ImageMetadata} containing the converted
     * values.
     */
    public ImageMetadata convertMetadata( boolean forJPEG ) {
        return CIFFMetadataConverter.convert( this, forJPEG );
    }

    /**
     * {@inheritDoc}
     */
    public float getAperture() {
        final ImageMetaValue value = getValue( CIFF_SI_FNUMBER );
        return  value != null ?
                (float)MetadataUtil.convertFStopFromAPEX( value.getIntValue() ) : 0;
    }

    /**
     * {@inheritDoc}
     */
    public String getArtist() {
        final ImageMetaValue value = getValue( CIFF_OWNER_NAME );
        return value != null ? value.getStringValue() : null;
    }

    /**
     * {@inheritDoc}
     */
    public int getBitsPerChannel() {
        final ImageMetaValue value = getValue( CIFF_II_COMPONENT_BIT_DEPTH );
        return value != null ? value.getIntValue() : 0;
    }

    /**
     * {@inheritDoc}
     */
    public String getCameraMake( boolean includeModel ) {
        final ImageMetaValue value = getValue( CIFF_MAKE_MODEL );
        if ( value != null ) {
            String makeModel = value.getStringValue();
            final int makeNullPos = makeModel.indexOf( '\0' );
            if ( makeNullPos >= 0 ) {
                if ( includeModel ) {
                    final int modelPos = makeNullPos + 1;
                    int endPos = makeModel.indexOf( '\0', modelPos );
                    if ( endPos == -1 )
                        endPos = makeModel.length();
                    makeModel = MetadataUtil.undupMakeModel(
                        makeModel.substring( 0, makeNullPos ),
                        makeModel.substring( modelPos, endPos )
                    );
                }
                if ( !includeModel )
                    makeModel = makeModel.substring( 0, makeNullPos );
            }
            return makeModel.toUpperCase().trim();
        }
        return null;
    }

    /**
     * Gets the camera model only.
     *
     * @return Returns said model or <code>null</code> if unavailable.
     */
    public String getCameraModel() {
        final ImageMetaValue value = getValue( CIFF_MAKE_MODEL );
        if ( value == null )
            return null;
        String makeModel = value.getStringValue();
        final int nullPos = makeModel.indexOf( '\0' );
        if ( nullPos == -1 )
            return null;
        final int modelPos = nullPos + 1;
        int endPos = makeModel.indexOf( '\0', modelPos );
        if ( endPos == -1 )
            endPos = makeModel.length();
        makeModel = makeModel.substring( modelPos, endPos );
        return makeModel.toUpperCase();
    }

    /**
     * {@inheritDoc}
     */
    public Date getCaptureDateTime() {
        final ImageMetaValue value = getValue( CIFF_CAPTURED_TIME );
        return  value instanceof DateMetaValue ?
                ((DateMetaValue)value).getDateValue() : null;
    }

    /**
     * {@inheritDoc}
     */
    public int getColorTemperature() {
        final ImageMetaValue value = getValue( CIFF_COLOR_TEMPERATURE );
        return value != null ? value.getIntValue() : 0;

    }

    /**
     * {@inheritDoc}
     */
    public float getFocalLength() {
        final ImageMetaValue value = getValue( CIFF_FL_FOCAL_LENGTH );
        return value != null ? value.getFloatValue() : 0;
    }

    /**
     * {@inheritDoc}
     */
    public int getImageHeight() {
        ImageMetaValue value = getValue( CIFF_II_IMAGE_HEIGHT );
        if ( value == null )
            value = getValue( CIFF_PI_IMAGE_HEIGHT );
        return value != null ? value.getIntValue() : 0;
    }

    /**
     * {@inheritDoc}
     */
    public int getImageWidth() {
        ImageMetaValue value = getValue( CIFF_II_IMAGE_WIDTH );
        if ( value == null )
            value = getValue( CIFF_PI_IMAGE_WIDTH );
        return value != null ? value.getIntValue() : 0;
    }

    /**
     * {@inheritDoc}
     */
    public int getISO() {
        final ImageMetaValue value = getValue( CIFF_SI_ISO );
        if ( value != null )
            return MetadataUtil.convertISOFromAPEX( value.getIntValue() );
        final String isoLabel = hasTagValueLabelFor( CIFF_CS_ISO );
        if ( isoLabel != null )
            try {
                //
                // The reason for using parseInt() is because most of the
                // labels are really just the true integer ISO values: the
                // metadata value is just a key for a look-up table.  However,
                // a couple of the labels are strings like "auto".  Hence, if
                // we can parse an integer successfully, that's the true ISO
                // value; if not, we return 0.
                //
                return Integer.parseInt( isoLabel );
            }
            catch ( NumberFormatException e ) {
                // ignore
            }
        return 0;
    }

    /**
     * {@inheritDoc}
     */
    public String getLens() {
        final ImageMetaValue lensValue = getValue( CIFF_CS_LENS_TYPE );
        final String label = hasTagValueLabelFor( lensValue );
        if ( label != null )
            return label;
        return makeLensLabelFrom(
            getValue( CIFF_CS_SHORT_FOCAL_LENGTH ),
            getValue( CIFF_CS_LONG_FOCAL_LENGTH ),
            getValue( CIFF_CS_FOCAL_UNITS_PER_MM )
        );
    }

    /**
     * Gets the name of this directory.
     *
     * @return Always returns &quot;CIFF&quot;.
     */
    public String getName() {
        return "CIFF";
    }

    /**
     * {@inheritDoc}
     */
    public ImageOrientation getOrientation() {
        int orientation;
        ImageMetaValue value = getValue( CIFF_II_ROTATION );
        if ( value != null ) {
            orientation = value.getIntValue();
            if ( orientation < 0 )
                orientation += 360;
            switch ( orientation ) {
                case 0:
                    return ORIENTATION_LANDSCAPE;
                case 90:
                    return ORIENTATION_90CCW;
                case 180:
                    return ORIENTATION_180;
                case 270:
                    return ORIENTATION_90CW;
            }
        }
        value = getValue( CIFF_SI_AUTO_ROTATE );
        if ( value != null )
            switch ( value.getIntValue() ) {
                case CIFF_AUTO_ROTATE_NONE:
                    return ORIENTATION_LANDSCAPE;
                case CIFF_AUTO_ROTATE_90CCW:
                    return ORIENTATION_90CCW;
                case CIFF_AUTO_ROTATE_180:
                    return ORIENTATION_180;
                case CIFF_AUTO_ROTATE_90CW:
                    return ORIENTATION_90CW;
            }
        return ORIENTATION_UNKNOWN;
    }

    /**
     * {@inheritDoc}
     */
    public float getShutterSpeed() {
        final ImageMetaValue value = getValue( CIFF_SI_SHUTTER_SPEED );
        if ( value == null )
            return 0;
        final int apex = value.getIntValue();
        final Rational speed = MetadataUtil.convertShutterSpeedFromAPEX( apex );
        return speed.floatValue();
    }

    /**
     * {@inheritDoc}
     */
    public ImageMetaTagInfo getTagInfoFor( Integer id ) {
        return m_tagsByID.get( id );
    }

    /**
     * {@inheritDoc}
     */
    public ImageMetaTagInfo getTagInfoFor( String name ) {
        return m_tagsByName.get( name );
    }

    /**
     * Puts a key/value pair into this directory.  For a Canon tag that has
     * subfields, expands the values into multiple key/value pairs.
     *
     * @param tagID The metadata tag ID (the key).
     * @param value The value to put.
     * @see #valueToString(ImageMetaValue)
     */
    public void putValue( Integer tagID, ImageMetaValue value ) {
        switch ( tagID ) {
            case CIFF_BASE_ISO:
                //
                // The base ISO has "junk" values past the value of interest
                // (which is the first), so discard them.
                //
                value = new UnsignedShortMetaValue(
                    value.getUnsignedShortValue()
                );
                break;
            case CIFF_CAMERA_SETTINGS:
            case CIFF_CUSTOM_FUNCTIONS:
            case CIFF_SHOT_INFO:
            case CIFF_SENSOR_INFO:
                explodeSubfields( tagID, 1, value, false );
                return;
            case CIFF_CS_ISO:
                if ( value.getIntValue() == 0 )
                    return;             // Use CIFF_SI_ISO instead.
                break;
            case CIFF_IMAGE_FORMAT:
                //
                // The image format has "junk" values past the value of
                // interest (which is the first), so discard them.
                //
                value = new UnsignedLongMetaValue( value.getLongValue() );
                break;
            case CIFF_SI_ISO:
                if ( value.getIntValue() == 0 )
                    return;             // Use EXIF_ISO_SPEED_RATINGS instead.
                break;
            case CIFF_FOCAL_LENGTH:
            case CIFF_PICTURE_INFO:
                explodeSubfields( tagID, 1, value, true );
                return;
            case CIFF_IMAGE_INFO:
                explodeSubfields( tagID, 0, value, false );
                return;
            case CIFF_WHITE_BALANCE_TABLE:
                // TODO: handle this case
                return;
        }
        super.putValue( tagID, value );
    }

    /**
     * This method allows an <code>ImageMetadataDirectory</code> to alter the
     * <code>toString()</code> value of an {@link ImageMetaValue}.
     * <p>
     * In the case of the <code>CIFFDirectory</code>, some tags have subfields
     * and those subfields' integer values mean something.  This overridden
     * method substitutes what the values mean.
     *
     * @param value The {@link ImageMetaValue} whose value to convert to a
     * {@link String}.
     * @return Returns a string if said string is to be used for the
     * <code>toString()</value> of the {@link ImageMetaValue};
     * <code>null</code> otherwise.
     * @see #putValue(Integer,ImageMetaValue)
     */
    public String valueToString( ImageMetaValue value ) {
        switch ( value.getOwningTagID() ) {
            case CIFF_CS_LONG_FOCAL_LENGTH:
            case CIFF_CS_SHORT_FOCAL_LENGTH:
            case CIFF_FL_FOCAL_LENGTH:
                return value.getStringValue() + "mm";   // TODO: localize "mm"
            case CIFF_MAKE_MODEL:
                return getCameraMake( true );
            case CIFF_SI_BULB_DURATION: {
                final int n = value.getIntValue();
                return TextUtil.tenths( n / 10F );
            }
            case CIFF_SI_FNUMBER: {
                final int apex = value.getIntValue();
                return TextUtil.tenths(
                    MetadataUtil.convertFStopFromAPEX( apex )
                );
            }
            case CIFF_SI_ISO: {
                final int apex = value.getIntValue();
                return Integer.toString(
                    MetadataUtil.convertISOFromAPEX( apex )
                );
            }
            case CIFF_SI_SHUTTER_SPEED: {
                final int apex = value.getIntValue();
                final Rational speed =
                    MetadataUtil.convertShutterSpeedFromAPEX( apex );
                return MetadataUtil.shutterSpeedString( speed.doubleValue() );
            }
            default:
                return super.valueToString( value );
        }
    }

    ////////// protected //////////////////////////////////////////////////////

    /**
     * Gets the priority of this directory for providing the metadata supplied
     * by implementing the given provider interface.
     * <p>
     * The priority is guaranteed to be lower than the priority for
     * {@link EXIFDirectory} and {@link TIFFDirectory}.
     *
     * @param provider The provider interface to get the priority for.
     * @return Returns said priority.
     */
    protected int getProviderPriorityFor(
        Class<? extends ImageMetadataProvider> provider )
    {
        return 0;
    }

    /**
     * Get the {@link ResourceBundle} to use for tags.
     *
     * @return Returns said {@link ResourceBundle}.
     */
    protected ResourceBundle getTagLabelBundle() {
        return m_tagBundle;
    }

    /**
     * {@inheritDoc}
     */
    protected Class<? extends ImageMetaTags> getTagsInterface() {
        return CIFFTags.class;
    }

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

    /**
     * Add the tag mappings.
     *
     * @param id The tag's ID.
     * @param name The tag's name.
     * @param type The tag's {@link ImageMetaType}.
     */
    private static void add( int id, String name, ImageMetaType type ) {
        final ImageMetaTagInfo tagInfo =
            new ImageMetaTagInfo( id, name, type, false );
        m_tagsByID.put( id, tagInfo );
        m_tagsByName.put( name, tagInfo );
    }

    /**
     * This is where the actual labels for the tags are.
     */
    private static final ResourceBundle m_tagBundle = ResourceBundle.getBundle(
        "com.lightcrafts.image.metadata.CIFFTags"
    );

    /**
     * A mapping of tags by ID.
     */
    private static final Map<Integer,ImageMetaTagInfo> m_tagsByID =
        new HashMap<Integer,ImageMetaTagInfo>();

    /**
     * A mapping of tags by name.
     */
    private static final Map<String,ImageMetaTagInfo> m_tagsByName =
        new HashMap<String,ImageMetaTagInfo>();

    static {
        add( CIFF_BASE_ISO, "BaseISO", META_USHORT );
        add( CIFF_BODY_ID, "SerialNumber", META_ULONG );
        add( CIFF_CAMERA_OBJECT, "CameraObject", META_UNDEFINED );
        add( CIFF_CAMERA_SETTINGS, "CameraSettings", META_UNDEFINED );
        add( CIFF_CAMERA_SPECIFICATION, "CameraSpecification", META_UNDEFINED );
        add( CIFF_CAPTURED_TIME, "CapturedTime", META_DATE );
        add( CIFF_COLOR_SPACE, "ColorSpace", META_USHORT );
        add( CIFF_COLOR_TEMPERATURE, "ColorTemperature", META_USHORT );
        add( CIFF_CS_AF_POINT_SELECTED, "CSAFPointSelected", META_USHORT );
        add( CIFF_CS_CONTINUOUS_DRIVE_MODE, "CSContinuousDriveMode", META_USHORT );
        add( CIFF_CS_CONTRAST, "CSContrast", META_USHORT );
        add( CIFF_CS_DIGITAL_ZOOM, "CSDigitalZoom", META_USHORT );
        add( CIFF_CS_EASY_SHOOTING_MODE, "CSEasyShootingMode", META_USHORT );
        add( CIFF_CS_EXPOSURE_MODE, "CSExposureMode", META_USHORT );
        add( CIFF_CS_FLASH_ACTIVITY, "CSFlashActivity", META_USHORT );
        add( CIFF_CS_FLASH_DETAILS, "CSFlashDetails", META_USHORT );
        add( CIFF_CS_FLASH_MODE, "CSFlashMode", META_USHORT );
        add( CIFF_CS_FOCAL_UNITS_PER_MM, "CSFocalUnitsPerMM", META_USHORT );
        add( CIFF_CS_FOCUS_MODE, "CSFocusMode", META_USHORT );
        add( CIFF_CS_FOCUS_MODE_G1, "CSFocusModeG1", META_USHORT );
        add( CIFF_CS_FOCUS_TYPE, "CSFocusType", META_USHORT );
        add( CIFF_CS_IMAGE_SIZE, "CSImageSize", META_USHORT );
        add( CIFF_CS_ISO, "CSISO", META_USHORT );
        add( CIFF_CS_LENS_TYPE, "CSLensType", META_USHORT );
        add( CIFF_CS_LONG_FOCAL_LENGTH, "CSLongFocalLength", META_USHORT );
        add( CIFF_CS_MACRO_MODE, "CSMacroMode", META_USHORT );
        add( CIFF_CS_METERING_MODE, "CSMeteringMode", META_USHORT );
        add( CIFF_CS_QUALITY, "CSQuality", META_USHORT );
        add( CIFF_CS_SATURATION, "CSSaturation", META_USHORT );
        add( CIFF_CS_SELF_TIMER_DELAY, "CSSelfTimerDelay", META_USHORT );
        add( CIFF_CS_SHARPNESS, "CSSharpness", META_USHORT );
        add( CIFF_CS_SHORT_FOCAL_LENGTH, "CSShortFocalLength", META_USHORT );
        add( CIFF_CS_ZOOMED_RESOLUTION, "CSZoomedResolution", META_USHORT );
        add( CIFF_CS_ZOOMED_RESOLUTION_BASE, "CSZoomedResolutionBase", META_USHORT );
        add( CIFF_CUSTOM_FUNCTIONS, "CustomFunctions", META_UNDEFINED );
        add( CIFF_DECODER_TABLE, "DecoderTable", META_ULONG );
        add( CIFF_EXIF_INFORMATION, "EXIFInformation", META_UNDEFINED );
        add( CIFF_EXPOSURE_INFO, "ExposureInfo", META_ULONG );
        add( CIFF_FILE_DESCRIPTION, "FileDescription", META_STRING );
        add( CIFF_FILE_NUMBER, "FileNumber", META_ULONG );
        add( CIFF_FIRMWARE_VERSION, "FirmwareVersion", META_STRING );
        add( CIFF_FLASH_INFO, "FlashInfo", META_ULONG );
        add( CIFF_FL_FOCAL_LENGTH, "FLFocalLength", META_USHORT );
        add( CIFF_FL_FOCAL_PLANE_X_SIZE, "FLFocalPlaneXSize", META_USHORT );
        add( CIFF_FL_FOCAL_PLANE_Y_SIZE, "FLFocalPlaneYSize", META_USHORT );
        add( CIFF_FOCAL_LENGTH, "FocalLength", META_UNDEFINED );
        add( CIFF_II_COLOR_BIT_DEPTH, "IIColorBitDepth", META_ULONG );
        add( CIFF_II_COLOR_BW, "IIColorBw", META_ULONG );
        add( CIFF_II_COMPONENT_BIT_DEPTH, "IIComponentBitDepTH", META_ULONG );
        add( CIFF_II_IMAGE_HEIGHT, "IIImageHeight", META_ULONG );
        add( CIFF_II_IMAGE_WIDTH, "IIImageWidth", META_ULONG );
        add( CIFF_II_PIXEL_ASPECT_RATIO, "IIPixelAspectRatio", META_ULONG );
        add( CIFF_II_ROTATION, "IIRotation", META_ULONG );
        add( CIFF_IMAGE_DESCRIPTION, "ImageDescription", META_UNDEFINED );
        add( CIFF_IMAGE_FILE_NAME, "ImageFileName", META_STRING );
        add( CIFF_IMAGE_FORMAT, "ImageFormat", META_ULONG );
        add( CIFF_IMAGE_INFO, "ImageInfo", META_ULONG );
        add( CIFF_IMAGE_PROPS, "ImageProps", META_UNDEFINED );
        add( CIFF_IMAGE_TYPE, "ImageType", META_STRING );
        add( CIFF_JPG_FROM_RAW, "JPGFromRaw", META_UNDEFINED );
        add( CIFF_MAKE_MODEL, "MakeModel", META_STRING );
        add( CIFF_MEASURED_EV, "MeasuredEv", META_ULONG );
        add( CIFF_MEASURED_INFO, "MeasuredInfo", META_UNDEFINED );
        add( CIFF_OWNER_NAME, "OwnerName", META_STRING );
        add( CIFF_PICTURE_INFO, "PictureInfo", META_UNDEFINED );
        add( CIFF_PI_AF_POINTS_USED, "PIAfPointsUsed", META_USHORT );
        add( CIFF_PI_IMAGE_HEIGHT, "PIImageHeight", META_USHORT );
        add( CIFF_PI_IMAGE_HEIGHT_AS_SHOT, "PIImageHeightAsShot", META_USHORT );
        add( CIFF_PI_IMAGE_WIDTH, "PIImageWidth", META_USHORT );
        add( CIFF_PI_IMAGE_WIDTH_AS_SHOT, "PIImageWidthAsShot", META_USHORT );
        add( CIFF_PREVIEW_IMAGE_LENGTH, "PreviewImageLength", META_ULONG );
        add( CIFF_PREVIEW_IMAGE_OFFSET, "PreviewImageOffset", META_ULONG );
        add( CIFF_RAW_DATA, "RawData", META_UNDEFINED );
        add( CIFF_RECORD_ID, "RecordID", META_ULONG );
        add( CIFF_RELEASE_SETTING, "ReleaseSetting", META_USHORT );
        add( CIFF_ROM_OPERATION_MODE, "ROMOperationMode", META_STRING );
        add( CIFF_SELF_TIMER_TIME, "SelfTimerTime", META_ULONG );
        add( CIFF_SENSOR_INFO, "SensorInfo", META_UNDEFINED );
        add( CIFF_SHOOTING_RECORD, "ShootingRecord", META_UNDEFINED );
        add( CIFF_SHOT_INFO, "ShotInfo", META_UNDEFINED );
        add( CIFF_SHUTTER_RELEASE_METHOD, "ShutterReleaseMethod", META_USHORT );
        add( CIFF_SHUTTER_RELEASE_TIMING, "ShutterReleaseTiming", META_USHORT );
        add( CIFF_SI_AF_POINT_USED, "SIAfPointUsed", META_USHORT );
        add( CIFF_SI_AUTO_EXPOSURE_BRACKETING, "SIAutoExposureBracketing", META_USHORT );
        add( CIFF_SI_AUTO_ROTATE, "SIAutoRotate", META_USHORT );
        add( CIFF_SI_BULB_DURATION, "SIBulbDuration", META_USHORT );
        add( CIFF_SI_EXPOSURE_COMPENSATION, "SIExposureCompensation", META_USHORT );
        add( CIFF_SI_FLASH_BIAS, "SIFlashBias", META_USHORT );
        add( CIFF_SI_FNUMBER, "FNumber", META_USHORT );
        add( CIFF_SI_FOCUS_DISTANCE_LOWER, "SIFocusDistanceLower", META_USHORT );
        add( CIFF_SI_FOCUS_DISTANCE_UPPER, "SIFocusDistanceUpper", META_USHORT );
        add( CIFF_SI_ISO, "SIISO", META_USHORT );
        add( CIFF_SI_SEQUENCE_NUMBER, "SISequenceNumber", META_USHORT );
        add( CIFF_SI_SHUTTER_SPEED, "SIShutterSpeed", META_USHORT );
        add( CIFF_SI_WHITE_BALANCE, "SIWhiteBalance", META_USHORT );
        add( CIFF_SSI_SENSOR_BOTTOM_BORDER, "SSISensorBottomBorder", META_USHORT );
        add( CIFF_SSI_SENSOR_HEIGHT, "SSISensorHeight", META_USHORT );
        add( CIFF_SSI_SENSOR_LEFT_BORDER, "SSISensorLeftBorder", META_USHORT );
        add( CIFF_SSI_SENSOR_RIGHT_BORDER, "SSISensorRightBorder", META_USHORT );
        add( CIFF_SSI_SENSOR_TOP_BORDER, "SSISensorTopBorder", META_USHORT );
        add( CIFF_SSI_SENSOR_WIDTH, "SSISensorWidth", META_USHORT );
        add( CIFF_TARGET_DISTANCE_SETTING, "TargetDistanceSetting", META_ULONG );
        add( CIFF_TARGET_IMAGE_TYPE, "TargetImageType", META_USHORT );
        add( CIFF_THUMBNAIL_FILE_NAME, "ThumbnailFileName", META_STRING );
        add( CIFF_THUMBNAIL_IMAGE, "ThumbnailImage", META_UNDEFINED );
        add( CIFF_USER_COMMENT, "UserComment", META_STRING );
        add( CIFF_WHITE_BALANCE_TABLE, "WhiteBalanceTable", META_UNDEFINED );
    }
}
/* vim:set et sw=4 ts=4: */
