package Travel::Status::MOTIS::Trip;

use strict;
use warnings;
use 5.020;

use parent 'Class::Accessor';

use DateTime::Format::ISO8601;

use Travel::Status::MOTIS::Stop;
use Travel::Status::MOTIS::Polyline qw(decode_polyline);

our $VERSION = '0.04';

Travel::Status::MOTIS::Trip->mk_ro_accessors(
	qw(
	  id
	  mode
	  agency
	  display_name
	  route_color
	  route_text_color
	  headsign

	  is_realtime
	  is_cancelled

	  arrival
	  scheduled_arrival
	  realtime_arrival

	  departure
	  scheduled_departure
	  realtime_departure
	)
);

sub new {
	my ( $obj, %opt ) = @_;

	my $json = $opt{json}{legs}[0];
	my $time_zone = $opt{time_zone};

	my $ref = {
		id               => $json->{tripId},
		mode             => $json->{mode},
		agency           => $json->{agencyName},
		display_name     => $json->{displayName},
		route_color      => $json->{routeColor},
		route_text_color => $json->{routeTextColor},
		headsign         => $json->{headsign},

		is_cancelled => $json->{cancelled},
		is_realtime  => $json->{realTime},

		raw_stopovers =>
		  [ $json->{from}, @{ $json->{intermediateStops} }, $json->{to} ],
		raw_polyline => $json->{legGeometry},

		time_zone    => $time_zone,
	};

	$ref->{scheduled_departure} = DateTime::Format::ISO8601->parse_datetime(
		$json->{scheduledStartTime} );
	$ref->{scheduled_departure}->set_time_zone( $time_zone );

	if ( $json->{realTime} ) {
		$ref->{realtime_departure}
		  = DateTime::Format::ISO8601->parse_datetime( $json->{startTime} );
		$ref->{realtime_departure}->set_time_zone( $time_zone );
	}

	$ref->{departure} = $ref->{realtime_departure}
	  // $ref->{scheduled_departure};

	$ref->{scheduled_arrival}
	  = DateTime::Format::ISO8601->parse_datetime( $json->{scheduledEndTime} );
	$ref->{scheduled_arrival}->set_time_zone( $time_zone );

	if ( $json->{realTime} ) {
		$ref->{realtime_arrival}
		  = DateTime::Format::ISO8601->parse_datetime( $json->{endTime} );
		$ref->{realtime_arrival}->set_time_zone( $time_zone );
	}

	$ref->{arrival} = $ref->{realtime_arrival} // $ref->{scheduled_arrival};

	bless( $ref, $obj );

	return $ref;
}

sub polyline {
	my ($self) = @_;

	if ( not $self->{raw_polyline} ) {
		return;
	}

	if ( $self->{polyline} ) {
		return @{ $self->{polyline} };
	}

	my $polyline = [ decode_polyline( $self->{raw_polyline} ) ];

	my $gis_distance;

	eval {
		require GIS::Distance;
		$gis_distance = GIS::Distance->new;
	};

	if ($gis_distance) {
		my %minimum_distances;

		for my $stopover ( $self->stopovers ) {
			my $stop = $stopover->stop;

			for my $polyline_index ( 0 .. $#{$polyline} ) {
				my $coordinate = $polyline->[$polyline_index];
				my $distance   = $gis_distance->distance_metal(
					$stop->{lat},       $stop->{lon},
					$coordinate->{lat}, $coordinate->{lon},
				);

				if ( not $minimum_distances{ $stop->id }
					or $minimum_distances{ $stop->id }{distance} > $distance )
				{
					$minimum_distances{ $stop->id } = {
						distance => $distance,
						index    => $polyline_index,
					};
				}
			}
		}

		for my $stopover ( $self->stopovers ) {
			my $stop = $stopover->stop;

			if ( $minimum_distances{ $stop->id } ) {
				$polyline->[ $minimum_distances{ $stop->id }{index} ]{stop}
				  = $stop;
			}
		}
	}

	$self->{polyline} = $polyline;

	return @{ $self->{polyline} };
}

sub stopovers {
	my ($self) = @_;

	if ( $self->{stopovers} ) {
		return @{ $self->{stopovers} };
	}

	@{ $self->{stopovers} } = map {
		Travel::Status::MOTIS::Stopover->new(
			json      => $_,
			realtime  => $self->{is_realtime},
			time_zone => $self->{time_zone},
		)
	} ( @{ $self->{raw_stopovers} // [] } );

	return @{ $self->{stopovers} };
}

sub TO_JSON {
	my ($self) = @_;

	# transform raw_stopovers into stopovers (lazy accessor)
	$self->stopovers;

	# transform raw_polyline into polyline (lazy accessor)
	$self->polyline;

	my $ret = { %{$self} };

	for my $timestamp_key (
		qw(
		arrival
		scheduled_arrival
		realtime_arrival

		departure
		scheduled_departure
		realtime_departure
		)
	  )
	{
		if ( $ret->{$timestamp_key} ) {
			$ret->{$timestamp_key} = $ret->{$timestamp_key}->epoch;
		}
	}

	return $ret;
}

1;
