The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package main;

use 5.006002;

use strict;
use warnings;

use Test::More 0.88;

use lib qw{ inc };
use Astro::App::Satpass2::Test::App;	# For environment clean-up.

use Astro::App::Satpass2::Format::Template;

use Astro::Coord::ECI 0.059;
use Astro::Coord::ECI::Moon 0.059;
use Astro::Coord::ECI::Sun 0.059;
use Astro::Coord::ECI::TLE 0.059 qw{ :constants };
use Astro::Coord::ECI::TLE::Iridium 0.059;
use Astro::Coord::ECI::Utils 0.059 qw{ deg2rad };

use Cwd ();
use Time::Local;

my $sta = Astro::Coord::ECI->new()->geodetic(
    deg2rad( 38.898748 ),
    deg2rad( -77.037684 ),
    16.68 / 1000,
)->set( name => '1600 Pennsylvania Ave NW Washington DC 20502' );
my $sun = Astro::Coord::ECI::Sun->new();
my $moon = Astro::Coord::ECI::Moon->new();
# The following TLE is from
# SPACETRACK REPORT NO. 3
# Models for Propagation of NORAD Element Sets
# Felix R. Hoots and Ronald L. Roehrich
# December 1980
# Compiled by TS Kelso
# 31 December 1988
# Obtained from celestrak.com
# NASA line added by T. R. Wyant
my ( $sat ) = Astro::Coord::ECI::TLE->parse( <<'EOD' );
None
1 88888U          80275.98708465  .00073094  13844-3  66816-4 0    8
2 88888  72.8435 115.9689 0086731  52.6988 110.5714 16.05824518  105
EOD

$sat->rebless( 'iridium' );

my $ft = Astro::App::Satpass2::Format::Template->new()->gmt( 1 );

# Encapsulation violation. The _uniq() subroutine may be moved or
# retracted without notice of any kind.

is_deeply
    [ Astro::App::Satpass2::Format::Template::_uniq(
	    qw{ Able was I ere I saw Elba } ) ],
    [ qw{ Able was I ere saw Elba } ],
    'Check our implementation of uniq()';

ok $ft, 'Instantiate Astro::App::Satpass2::Format::Template';

ok $ft->template( fubar => <<'EOD' ), 'Can set custom template';
Able was [% arg.0 %] ere [% arg.0 %] saw Elba
EOD

is $ft->template( 'fubar' ), <<'EOD', 'Can get same template back';
Able was [% arg.0 %] ere [% arg.0 %] saw Elba
EOD

is $ft->format(
    template	=> 'fubar',
    arg		=> [ 'I' ],
), <<'EOD', 'Can use custom template';
Able was I ere I saw Elba
EOD

is $ft->format(
    template	=> 'almanac',
    data	=> [ {
		almanac	=> {
		    description	=> 'Moon rise',
		    detail		=> 1,
		    event		=> 'horizon',
		},
		body	=> $moon,
		station	=> $sta,
		time	=> timegm( 8, 38, 9, 1, 3, 111 ),
	    },
	    {
		almanac	=> {
		    description	=> 'Moon transits meridian',
		    detail		=> 1,
		    event		=> 'transit',
		},
		body	=> $moon,
		station	=> $sta,
		time	=> timegm( 20, 46, 15, 1, 3, 111 ),
	    },
	    {
		almanac	=> {
		    description	=> 'Moon set',
		    detail		=> 0,
		    event		=> 'horizon',
		},
		body	=> $moon,
		station	=> $sta,
		time	=> timegm( 40, 2, 22, 1, 3, 111 ),
	    },
	] ), <<'EOD', 'Almanac';
2011-04-01 09:38:08 Moon rise
2011-04-01 15:46:20 Moon transits meridian
2011-04-01 22:02:40 Moon set
EOD

is $ft->format(
    template	=> 'flare',
    data	=> [
	    {
		angle => 0.262059013150469,
		appulse => {
		    angle => 1.02611236331053,
		    body => $sun,
		},
		area => 5.01492326975883e-12,
		azimuth => 2.2879991425019,
		body => $sat,
		center => {
		    body => Astro::Coord::ECI->new()->eci(
			-239.816850881829,
			4844.88846601786,
			4147.86073518313,
		    ),
		    magnitude => -9.19948076848716,
		},
		elevation => 0.494460647040746,
		magnitude => 3.92771062285379,
		mma => 0,
		range => 410.943432358706,
		specular => 0,
		station => $sta,
		status => '',
		time => timegm( 44, 7, 10, 13, 9, 80 ) + .606786,
		type => 'am',
		virtual_image => Astro::Coord::ECI->new()->eci(
		    -126704974.030369,
		    66341250.3306362,
		    -42588590.3666171,
		),
	    },
	] ), <<'EOD', 'Flare';
                                                     Degre
                                                      From   Center Center
Time     Name         Eleva  Azimuth      Range Magn   Sun  Azimuth  Range
1980-10-13
10:07:45 None          28.3 131.1 SE      410.9  3.9 night 300.8 NW  412.5
EOD

is $ft->format(
    template	=> 'list',
    data	=> [ $sat ]
), <<'EOD', 'List';
   OID Name                     Epoch               Period
 88888 None                     1980-10-01 23:41:24 01:29:37
EOD

{
    $ft->template( list => <<'EOD' );
[%- title.oid( align_left = 0 ) %] [% title.name %]
[% FOR item IN data %]
    [%- item.oid %] [% item.name %]
[% END -%]
EOD

    is $ft->format(
	template	=> 'list',
	data		=> [ $sat ],
    ), <<'EOD', 'List (custom format)';
   OID Name
 88888 None
EOD

    is $ft->format(
	template	=> 't/list.tt',
	data		=> [ $sat ],
    ), <<'EOD', 'List (format from relative path)';
                    Name OID
                    None  88888
EOD

    my $abs = Cwd::abs_path( 't/list.tt' );

    my $rslt;
    eval {
	$ft->format(
	    template	=> $abs,
	    data	=> [ $sat ],
	);
	1;
    } or do {
	$rslt = $@;
    };
    like $rslt, qr{absolute paths are not allowed}sm,
	    'List (format from absolute path) should fail by default'
	or diag defined $rslt ? "Failed but with error $rslt" :
    'Succeeded';

    $ft->permissive( 1 );

    is $ft->format(
	template	=> $abs,
	data		=> [ $sat ],
    ), <<'EOD', 'List (format from absolute path, permissive)';
                    Name OID
                    None  88888
EOD

}

is $ft->format(
    template	=> 'location',
    data	=> $sta
), <<'EOD', 'Location';
Location: 1600 Pennsylvania Ave NW Washington DC 20502
          Latitude 38.8987, longitude -77.0377, height 17 m
EOD

is $ft->format(
    template	=> 'pass',
    data	=> [
	    {
		body	=> $sat,
		events	=> [
		    {
			azimuth => 2.72679983099103,
			body => $sat,
			elevation => 0.350867451859261,
			event => PASS_EVENT_RISE,
			illumination => PASS_EVENT_LIT,
			range => 537.930341183133,
			station => $sta,
			time	=> timegm( 14, 7, 10, 13, 9, 80 ),
		    },
		    {
			azimuth		=> 2.22028221624351,
			body		=> $sat,
			elevation	=> 0.507347011634507,
			event		=> PASS_EVENT_BRIGHTEST,
			illumination	=> PASS_EVENT_LIT,
			range		=> 402.657696214206,
			station		=> $sta,
			time		=> timegm( 48, 7, 10, 13, 9, 80 ),
		    },
		    {
			azimuth => 1.95627424522813,
			body => $sat,
			elevation => 0.535869703007124,
			event => PASS_EVENT_MAX,
			illumination => PASS_EVENT_LIT,
			range => 385.864099675914,
			station => $sta,
			time => timegm( 0, 8, 10, 13, 9, 80 ),
		    },
		    {
			azimuth => 0.988652345285029,
			body => $sat,
			elevation => 0.344817448574959,
			event => PASS_EVENT_SET,
			illumination => PASS_EVENT_LIT,
			range => 552.731309464471,
			station => $sta,
			time => timegm( 56, 8, 10, 13, 9, 80 ),
		    },
		],
		time => timegm( 0, 8, 10, 13, 9, 80 ),
	    },
	] ), <<'EOD', 'Pass';
    Time Eleva  Azimuth      Range Latitude Longitude Altitud Illum Event

1980-10-13     88888 - None
10:07:14  20.1 156.2 SE      537.9  34.8367  -74.8798   204.0 lit   rise
10:07:48  29.1 127.2 SE      402.7  36.9992  -73.9844   204.9 lit   brgt
10:08:00  30.7 112.1 E       385.9  37.7599  -73.6545   205.2 lit   max
10:08:56  19.8  56.6 NE      552.7  41.2902  -72.0053   207.0 lit   set
EOD

is $ft->format(
    template	=> 'pass_events',
    data	=> [
	    {
		body	=> $sat,
		events	=> [
		    {
			azimuth => 2.72679983099103,
			body => $sat,
			elevation => 0.350867451859261,
			event => PASS_EVENT_RISE,
			illumination => PASS_EVENT_LIT,
			range => 537.930341183133,
			station => $sta,
			time	=> timegm( 14, 7, 10, 13, 9, 80 ),
		    },
		    {
			azimuth => 1.95627424522813,
			body => $sat,
			elevation => 0.535869703007124,
			event => PASS_EVENT_MAX,
			illumination => PASS_EVENT_LIT,
			range => 385.864099675914,
			station => $sta,
			time => timegm( 0, 8, 10, 13, 9, 80 ),
		    },
		    {
			azimuth => 0.988652345285029,
			body => $sat,
			elevation => 0.344817448574959,
			event => PASS_EVENT_SET,
			illumination => PASS_EVENT_LIT,
			range => 552.731309464471,
			station => $sta,
			time => timegm( 56, 8, 10, 13, 9, 80 ),
		    },
		],
		time => timegm( 0, 8, 10, 13, 9, 80 ),
	    },
	] ), <<'EOD', 'Pass';
Date       Time     OID    Event Illum Eleva  Azimuth      Range
1980-10-13 10:07:14  88888 rise  lit    20.1 156.2 SE      537.9
1980-10-13 10:08:00  88888 max   lit    30.7 112.1 E       385.9
1980-10-13 10:08:56  88888 set   lit    19.8  56.6 NE      552.7
EOD

$moon->universal( timegm( 0, 0, 4, 1, 3, 111 ) );
is $ft->format(
    template	=> 'phase',
    data	=> [ { body => $moon, time => $moon->universal() } ]
), <<'EOD', 'Phase';
      Date     Time     Name Phas Phase             Lit
2011-04-01 04:00:00     Moon  333 waning crescent     5%
EOD

is $ft->format(
    template	=> 'position',
    data	=> {
	    bodies	=> [ $sat, $moon ],
	    station	=> $sta,
	    time	=> timegm( 45, 7, 10, 13, 9, 80 ),
	} ), <<'EOD', 'Position';
1980-10-13 10:07:45
            Name Eleva  Azimuth      Range               Epoch Illum
            None  28.4 130.7 SE      409.9 1980-10-01 23:41:24 lit
                                           MMA 0 mirror angle 15.0 magnitude 3.9
                                           MMA 1 Geometry does not allow reflection
                                           MMA 2 Geometry does not allow reflection
            Moon -55.8  59.2 NE   406685.1
EOD

$ft->local_coord( 'azel' );
is $ft->format(
    template	=> 'position',
    data	=> {
	    bodies	=> [ $sat, $moon ],
	    station	=> $sta,
	    time	=> timegm( 45, 7, 10, 13, 9, 80 ),
	} ), <<'EOD', 'Position, local_coord = azel';
1980-10-13 10:07:45
            Name Eleva  Azimuth               Epoch Illum
            None  28.4 130.7 SE 1980-10-01 23:41:24 lit
                                MMA 0 mirror angle 15.0 magnitude 3.9
                                MMA 1 Geometry does not allow reflection
                                MMA 2 Geometry does not allow reflection
            Moon -55.8  59.2 NE
EOD

$ft->local_coord( 'az_rng' );
is $ft->format(
    template	=> 'position',
    data	=> {
	    bodies	=> [ $sat, $moon ],
	    station	=> $sta,
	    time	=> timegm( 45, 7, 10, 13, 9, 80 ),
	} ), <<'EOD', 'Position, local_coord = az_rng';
1980-10-13 10:07:45
            Name  Azimuth      Range               Epoch Illum
            None 130.7 SE      409.9 1980-10-01 23:41:24 lit
                                     MMA 0 mirror angle 15.0 magnitude 3.9
                                     MMA 1 Geometry does not allow reflection
                                     MMA 2 Geometry does not allow reflection
            Moon  59.2 NE   406685.1
EOD

$ft->local_coord( 'equatorial' );
is $ft->format(
    template	=> 'position',
    data	=> {
	    bodies	=> [ $sat, $moon ],
	    station	=> $sta,
	    time	=> timegm( 45, 7, 10, 13, 9, 80 ),
	} ), <<'EOD', 'Position, local_coord = equatorial';
1980-10-13 10:07:45
                    Right
            Name Ascensio Decli               Epoch Illum
            None 09:17:51  -8.5 1980-10-01 23:41:24 lit
                                MMA 0 mirror angle 15.0 magnitude 3.9
                                MMA 1 Geometry does not allow reflection
                                MMA 2 Geometry does not allow reflection
            Moon 16:26:42 -17.2
EOD

$ft->local_coord( 'equatorial_rng' );
is $ft->format(
    template	=> 'position',
    data	=> {
	    bodies	=> [ $sat, $moon ],
	    station	=> $sta,
	    time	=> timegm( 45, 7, 10, 13, 9, 80 ),
	} ), <<'EOD', 'Position, local_coord = equatorial_rng';
1980-10-13 10:07:45
                    Right
            Name Ascensio Decli      Range               Epoch Illum
            None 09:17:51  -8.5      409.9 1980-10-01 23:41:24 lit
                                           MMA 0 mirror angle 15.0 magnitude 3.9
                                           MMA 1 Geometry does not allow reflection
                                           MMA 2 Geometry does not allow reflection
            Moon 16:26:42 -17.2   406685.1
EOD

is $ft->format(
	arg	=> [ qw{ sailor } ],
	template => \"Hello, [% arg.0 %]!\n",
    ), <<'EOD', 'Report';
Hello, sailor!
EOD

# NOTE: At this point, the local coordinates are equatorial_rng. We do
# not use them for subsequent tests, but if we do will probably need to
# reset them.

is $ft->format(
    template	=> 'tle',
    data	=> [ $sat ],
), <<'EOD', 'Tle';
None
1 88888U          80275.98708465  .00073094  13844-3  66816-4 0    8
2 88888  72.8435 115.9689 0086731  52.6988 110.5714 16.05824518  105
EOD

is $ft->format(
    template	=> 'tle_verbose',
    data	=> [ $sat ],
), <<'EOD', 'Tle verbose';
OID: 88888
    Name: None
    International Launch Designator:
    Epoch: 1980-10-01 23:41:24 GMT
    Effective Date: <none> GMT
    Classification: U
    Mean Motion: 4.01456130 degrees/minute
    First Derivative: 1.26899306e-07 degrees/minute squared
    Second Derivative: 1.66908e-11 degrees/minute cubed
    B Star Drag: 6.68160e-05
    Ephemeris Type: 0
    Inclination: 72.8435 degrees
    Ascending Node: 07:43:53 in right ascension
    Eccentricity: 0.0086731
    Argument Of Perigee: 52.6988 degrees from ascending node
    Mean Anomaly: 110.5714 degrees
    Element Number: 8
    Revolutions At Epoch: 105
    Period: 01:29:37
    Semimajor Axis: 6634.0 kilometers
    Perigee: 198.3 kilometers
    Apogee: 313.4 kilometers
EOD

eval {
    my $magic_word = 'Plugh';
    $ft->add_formatter_method( {
	    default	=> {
		width	=> 6,
	    },
	    dimension	=> {
		dimension	=> 'string_pseudo_units',
	    },
	    fetch	=> sub {
		my ( $self, $name, $arg ) = @_;
		return qq["$self->{data}{magic_word}"];
	    },
	    name	=> 'magic_word',
	} );
    $ft->template( advent => q<A hollow voice says [% data.magic_word( width = '' ) %]> );
    is $ft->format(
	template	=> 'advent',
	data		=> {
	    magic_word	=> $magic_word,
	}
    ), qq{A hollow voice says "$magic_word"}, 'Add a formatter';
    1;
} or fail "Added formatter failed: $@";

done_testing;

1;

# ex: set textwidth=72 :