#!/usr/bin/env perl
use strict;
use warnings;
use 5.020;
use utf8;

our $VERSION = '0.14';

use Getopt::Long;
use List::Util qw(min);
use Travel::Status::DE::IRIS;
use Travel::Status::DE::DBWagenreihung;

my $developer_mode = 0;

binmode( STDOUT, ':encoding(utf-8)' );

sub show_help {
	my ($code) = @_;

	say "Usage: db-wagenreihung <station> <train number>";

	exit $code;
}

sub show_version {
	say "db-wagenreihung version ${VERSION}";

	exit 0;
}

GetOptions(
	'h|help'  => sub { show_help(0) },
	'devmode' => \$developer_mode,
	'version' => sub { show_version() },
) or show_help(1);

if ( @ARGV != 2 ) {
	show_help(1);
}

my ( $station, $train_number ) = @ARGV;

my $col_first  = "\e[38;5;11m";
my $col_mixed  = "\e[38;5;208m";
my $col_second = "\e[0m";          #"\e[38;5;9m";
my $col_reset  = "\e[0m";

my $res = Travel::Status::DE::IRIS->new(
	developer_mode => $developer_mode,
	station        => $station,
	with_related   => 1
);

if ( $res->errstr ) {
	say STDERR $res->errstr;
	exit 1;
}

my @trains = grep { $_->train_no eq $train_number } $res->results;

if ( @trains != 1 ) {
	say STDERR "Unable to find train in reported departures";
	exit 1;
}

my $wr = Travel::Status::DE::DBWagenreihung->new(
	departure      => $trains[0]->sched_departure || $trains[0]->sched_arrival,
	developer_mode => $developer_mode,
	train_number   => $train_number,
);

printf(
	"%s: %s → %s\n",
	join( ' / ', map { $wr->train_type . ' ' . $_ } $wr->train_numbers ),
	join(
		' / ',
		map {
			sprintf( '%s (%s)', $_->{name}, join( q{}, @{ $_->{sections} } ) )
		} $wr->origins
	),
	join(
		' / ',
		map {
			sprintf( '%s (%s)', $_->{name}, join( q{}, @{ $_->{sections} } ) )
		} $wr->destinations
	),
);

printf( "%s Gleis %s\n\n", $wr->station->{name}, $wr->platform );

for my $section ( $wr->sections ) {
	my $section_length = $section->length_percent;
	my $spacing_left   = int( ( $section_length - 2 ) / 2 ) - 1;
	my $spacing_right  = int( ( $section_length - 2 ) / 2 );

	if ( $section_length % 2 ) {
		$spacing_left++;
	}

	printf( "▏%s%s%s▕",
		( $spacing_left >= 0 )                  ? ' ' x $spacing_left  : q{},
		$section->name, ( $spacing_right >= 0 ) ? ' ' x $spacing_right : q{} );
}
print "\n";

my @start_percentages = map { $_->{position}{start_percent} } $wr->wagons;
if ( my $min_percentage = min @start_percentages ) {
	print ' ' x ( $min_percentage - 1 );
}
print $wr->direction == 100 ? '>' : '<';

for my $wagon ( $wr->wagons ) {
	my $wagon_length
	  = $wagon->{position}->{end_percent} - $wagon->{position}->{start_percent};
	my $spacing_left  = int( $wagon_length / 2 ) - 2;
	my $spacing_right = int( $wagon_length / 2 ) - 1;

	if ( $wagon_length % 2 ) {
		$spacing_left++;
	}

	my $wagon_desc = $wagon->number || '?';

	if ( $wagon->is_closed ) {
		$wagon_desc = 'X';
	}

	if ( $wagon->is_locomotive or $wagon->is_powercar ) {
		$wagon_desc = ' ■ ';
	}

	my $class_color = '';
	if ( $wagon->class_type == 1 ) {
		$class_color = $col_first;
	}
	elsif ( $wagon->class_type == 2 ) {
		$class_color = $col_second;
	}
	elsif ( $wagon->class_type == 12 ) {
		$class_color = $col_mixed;
	}

	printf( "%s%s%3s%s%s",
		' ' x $spacing_left, $class_color, $wagon_desc,
		$col_reset,          ' ' x $spacing_right );
}
print $wr->direction == 100 ? '>' : '<';
print "\n\n";

for my $group ( $wr->groups ) {
	if ( $group->has_sections ) {
		printf( "%s (%s)\n",
			$group->description || 'Zug',
			join( q{}, $group->sections ) );
	}
	else {
		say $group->description || 'Zug';
	}
	printf(
		"%s %s  %s → %s\n\n",
		$wr->train_type, $group->train_no,
		$group->origin,  $group->destination
	);

	for my $wagon ( $group->wagons ) {
		printf(
			"%3s: %3s %10s  %s\n",
			$wagon->is_closed       ? 'X'
			: $wagon->is_locomotive ? 'Lok'
			: $wagon->number || '?',
			$wagon->model || '???',
			$wagon->type,
			join( q{  }, $wagon->attributes )
		);
	}
	say "";
}

__END__

=head1 NAME

db-wagenreihung - Interface to Deutsche Bahn carriage formation API

=head1 SYNOPSIS

B<db-wagenreihung> I<station> I<train-number>

=head1 VERSION

version 0.14

This is beta software: API and output format may change without notice.

=head1 DESCRIPTION

db-wagenreihung shows the carriage formation (also known as coach sequence) of
train I<train-number> at station I<station> (must be a name or DS100 code) as
reported by the Deutsche Bahn Wagenreihung API. As of April 2024, it has mature
support for long-distance (IC/EC/ICE) trains and a growing number of regional
transport providers that also offer mostly correct carriage formation data.

It is not possible to request the carriage formation at a train's terminus
station.  This is a known limitation.

The departure of I<train-number> must be in the time range between now and
two hours in the future.

=head1 EXAMPLES

=over

=item db-wagenreihung 'Essen Hbf' 723

Show carriage formation of ICE 723 at Essen Hbf

=item db-wagenreihung TS 3259

Show carriage formation of IRE 3259 at Stuttgart Hbf

=back

=head1 DEPENDENCIES

=over

=item * JSON(3pm)

=item * LWP::UserAgent(3pm)

=item * Travel::Status::DE::IRIS(3pm)

=back

=head1 AUTHOR

Copyright (C) 2018-2024 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>

=head1 LICENSE

This program is licensed under the same terms as Perl itself.
