#!/usr/bin/env perl

=head1 NAME

astmoctool - Utility for manipulating MOC files using AST

=head1 USAGE

Command line options are processed in the order given.  These can either
be the names of files to read, or options giving other operations to
perform.  For example to load a file and then display information:

    astmoctool MOC.fits --info

Note: AST performs all MOC operations at a particular "MaxOrder" value.
This will be determined by the first loaded MOC or can be specified
via the "--max-order" option.

=head1 OPTIONS

=over 4

=item B<--info, -i>

Display information about the current MOC.

=item B<--output, -o> FILENAME

Write the current MOC to the given file.

=item B<--intersection> FILENAME

Perform an intersection between the current MOC and the MOC in the given file.

=item B<--subtract> FILENAME

Subtract the MOC in the given file from the current MOC.

=item B<--show>

Display a description of the current AST MOC object.

=item B<--max-order>

Set the maximum order.  This must be done before any MOC is loaded.

=back

=cut

use strict;

use IO::File;
use Pod::Usage;

use Starlink::AST;
use Starlink::ATL::MOC qw/read_moc_fits write_moc_fits/;

moc_tool(@ARGV);

sub moc_tool {
    my @args = @_;

    my $moc = undef;
    my $initialize_moc = sub {
        my $options = shift // '';
        $moc = Starlink::AST::Moc->new($options);
    };


    my %command = map {my ($opts, $func) = @$_; map {$_ => $func} @$opts} (
        [[qw/--info -i/], sub {
            die 'A MOC has not yet been defined' unless defined $moc;
            printf "Max. order: %i (%f\")\n", $moc->GetI('MaxOrder'), $moc->GetD('MaxRes');
            printf "Area: %f sq. deg.\n", $moc->GetD('MocArea') / 3600.0;
            printf "Cells: %i\n", $moc->GetI('MocLength');
            printf "Type: %i-byte\n", $moc->GetI('MocType');
        }],
        [[qw/--output -o/], sub {
            my $filename = shift @args;
            die 'No output filename specified' unless defined $filename;
            die "Output file '$filename' already exists" if -e $filename;
            die 'A MOC has not yet been defined' unless defined $moc;
            write_moc($moc, $filename);
        }],
        [[qw/--intersection/], sub {
            my $filename = shift @args;
            die 'No intersection filename specified' unless defined $filename;
            die 'A MOC has not yet been defined' unless defined $moc;
            read_moc($moc, $filename, Starlink::AST::Region::AST__AND(),  0);
        }],
        [[qw/--subtract/], sub {
            my $filename = shift @args;
            die 'No subtraction filename specified' unless defined $filename;
            die 'A MOC has not yet been defined' unless defined $moc;
            read_moc($moc, $filename, Starlink::AST::Region::AST__AND(),  1);
        }],
        [[qw/--show/], sub {
            die 'A MOC has not yet been defined' unless defined $moc;
            $moc->Show();
        }],
        [[qw/--max-order/], sub {
            my $max_order = shift @args;
            die 'No maximum order specified' unless defined $max_order;
            die 'Maximum order must be between 0 and 27'
                if $max_order < 0 or $max_order > 27;
            die 'Can not set maximum order: a MOC has already been defined'
                if defined $moc;
            $initialize_moc->(sprintf 'MaxOrder=%i', $max_order);
        }],
        [[qw/--help -h/], sub {
            pod2usage(-vebose => 1, -exitval => 'NOEXIT');
        }],
    );

    while (scalar @args) {
        my $arg = shift @args;

        if (exists $command{$arg}) {
            $command{$arg}->();
        }
        elsif (-e $arg) {
            $initialize_moc->() unless defined $moc;
            read_moc($moc, $arg, Starlink::AST::Region::AST__OR(),  0);
        }
        else {
            die "File or command '$arg' not found";
        }
    }
};

sub write_moc {
    my ($moc, $filename) = @_;

    if ($filename =~ /\.fits?$/i) {
        write_moc_fits($moc, $filename);
    }
    elsif ($filename eq '-') {
        print $moc->GetMocString(0), "\n";
    }
    else {
        my $json = $filename =~ /\.json$/i;

        my $fh = IO::File->new($filename, 'w');
        die "Could not open file '$filename' for writing" unless defined $fh;
        print $fh $moc->GetMocString($json ? 1 : 0);
        close $fh;
    }
}

sub read_moc {
    my ($moc, $filename, $mode, $negate) = @_;

    if ($filename =~ /\.fits?$/i) {
        read_moc_fits($filename, moc => $moc, mode => $mode, negate => $negate);
    }
    else {
        local $/;
        my $fh = IO::File->new($filename);
        die "Could not open file '$filename' for reading" unless defined $fh;
        my $buff = <$fh>;
        close $fh;

        $moc->AddMocString($mode, $negate, -1, $buff);
    }
}

__END__

=head1 COPYRIGHT

Copyright (C) 2019-2022 East Asian Observatory
All Rights Reserved.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
