#------------------------------------------------------------------------------
# File:         XISF.pm
#
# Description:  Read Extensible Image Serialization Format metadata
#
# Revisions:    2023-10-10 - P. Harvey Created
#
# References:   1) https://pixinsight.com/doc/docs/XISF-1.0-spec/XISF-1.0-spec.html
#------------------------------------------------------------------------------

package Image::ExifTool::XISF;

use strict;
use vars qw($VERSION);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::XMP;

$VERSION = '1.00';

# XISF tags (ref 1)
%Image::ExifTool::XISF::Main = (
    GROUPS => { 0 => 'XML', 1 => 'XML', 2 => 'Image' },
    VARS => { LONG_TAGS => 1 },
    NOTES => q{
        This table lists some standard Extensible Image Serialization Format (XISF)
        tags, but ExifTool will extract any other tags found.  See
        L<https://pixinsight.com/xisf/> for the specification.
    },
    ImageGeometry   => { },
    ImageSampleFormat => { },
    ImageBounds     => { },
    ImageImageType  => { Name => 'ImageType' },
    ImageColorSpace => { Name => 'ColorSpace' },
    ImageLocation   => { },
    ImageResolutionHorizontal => 'XResolution',
    ImageResolutionVertical => 'YResolution',
    ImageResolutionUnit => 'ResolutionUnit',
    ImageICCProfile => {
        Name => 'ICC_Profile',
        ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)',
        Binary => 1,
    },
    ImageICCProfileLocation => { Name => 'ICCProfileLocation' },
    ImagePixelStorage => { },
    ImageOffset      => { Name => 'ImagePixelOffset' },
    ImageOrientation => { Name => 'Orientation' },
    ImageId          => { Name => 'ImageID' },
    ImageUuid        => { Name => 'UUID' },
    ImageData        => { Binary => 1 },
    'CreationTime' => {
        Name => 'CreateDate',
        Shift => 'Time',
        Groups => { 2 => 'Time' },
        ValueConv => 'Image::ExifTool::XMP::ConvertXMPDate($val)',
        PrintConv => '$self->ConvertDateTime($val)',
    },
    CreatorApplication => { },
    Abstract        => { },
    AccessRights    => { },
    Authors         => { Groups => { 2 => 'Author' } },
    BibliographicReferences => { },
    BriefDescription => { },
    CompressionLevel => { },
    CompressionCodecs => { },
    Contributors    => { Groups => { 2 => 'Author' } },
    Copyright       => { Groups => { 2 => 'Author' } },
    CreatorModule   => { },
    CreatorOS       => { },
    Description     => { },
    Keywords        => { },
    Languages       => { },
    License         => { },
    OriginalCreationTime => {
        Name => 'DateTimeOriginal',
        Description => 'Date/Time Original',
        Shift => 'Time',
        Groups => { 2 => 'Time' },
        ValueConv => 'Image::ExifTool::XMP::ConvertXMPDate($val)',
        PrintConv => '$self->ConvertDateTime($val)',
    },
    RelatedResources => { },
    Title            => { },
);

#------------------------------------------------------------------------------
# Handle properties in XISF metadata structures
# Inputs: 0) attribute list ref, 1) attr hash ref,
#         2) property name ref, 3) property value ref
# Returns: true if value was changed
sub HandleXISFAttrs($$$$)
{
    my ($attrList, $attrs, $prop, $valPt) = @_;
    return 0 unless defined $$attrs{id};
    my ($changed, $a);
    # use "id" as the tag name, "value" as the value, and ignore "type"
    $$prop = $$attrs{id};
    $$prop =~ s/^XISF://;   # remove XISF namespace
    if (defined $$attrs{value}) {
        $$valPt = $$attrs{value};
        $changed = 1;
    }
    my @attrs = @$attrList;
    @$attrList = ( );
    foreach $a (@attrs) {
        if ($a eq 'id' or $a eq 'value' or $a eq 'type') {
            delete $$attrs{$a};
        } else {
            push @$attrList, $a;
        }
    }
    return $changed;
}

#------------------------------------------------------------------------------
# Read information in a XISF document
# Inputs: 0) ExifTool ref, 1) dirInfo ref
# Returns: 1 on success, 0 if this wasn't a valid XISF file
sub ProcessXISF($$)
{
    my ($et, $dirInfo) = @_;
    my $raf = $$dirInfo{RAF};
    my $buff;

    return 0 unless $raf->Read($buff, 16) == 16 and $buff =~ /^XISF0100/;
    $et->SetFileType();
    SetByteOrder('II');
    my $tagTablePtr = GetTagTable('Image::ExifTool::XISF::Main');
    my $hdrLen = Get32u(\$buff, 8);
    $raf->Read($buff, $hdrLen) == $hdrLen or $et->Warn('Error reading XISF header'), return 1;
    $et->FoundTag(XML => $buff);
    my %dirInfo = (
        DataPt => \$buff,
        IgnoreProp => { xisf => 1, Metadata => 1, Property => 1 },
        XMPParseOpts => { AttrProc => \&HandleXISFAttrs },
    );
    Image::ExifTool::XMP::ProcessXMP($et, \%dirInfo, $tagTablePtr);
    my $geo = $$et{VALUE}{ImageGeometry};
    if ($geo) {
        my ($w, $h, $n) = split /:/, $geo;
        $et->FoundTag(ImageWidth => $w);
        $et->FoundTag(ImageHeight => $h);
        $et->FoundTag(NumPlanes => $n);
    }
    return 1;
}

1;  # end

__END__

=head1 NAME

Image::ExifTool::XISF - Read Extensible Image Serialization Format metadata

=head1 SYNOPSIS

This module is used by Image::ExifTool

=head1 DESCRIPTION

This module contains definitions required by Image::ExifTool to read meta
information from XISF (Extensible Image Serialization Format) images.

=head1 AUTHOR

Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)

This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 REFERENCES

=over 4

=item L<https://pixinsight.com/doc/docs/XISF-1.0-spec/XISF-1.0-spec.html>

=back

=head1 SEE ALSO

L<Image::ExifTool::TagNames/XISF Tags>,
L<Image::ExifTool(3pm)|Image::ExifTool>

=cut

