-- Functions
create function calculate_dwell_time_seconds(arrival_time varchar, departure_time varchar) RETURNS int AS $$
    select extract(epoch from departure_time::interval) - extract(epoch from arrival_time::interval)
$$ LANGUAGE SQL IMMUTABLE;

create function predict_delay_seconds(position_delay int, stop_dwell_time_seconds smallint, provider_source_type varchar) RETURNS int AS $$
declare
    result int;
BEGIN
    select
        position_delay - least(greatest(0, position_delay), greatest(0, stop_dwell_time_seconds - 60))
    into result;

    if provider_source_type = '1' then -- http
        return greatest(0, result);
    else
        return result;
    end if;
END;
$$ LANGUAGE plpgsql;

comment on function predict_delay_seconds is '
Predicts the delay of a vehicle at a stop.
If the provider source type is HTTP, the predicted delay should never be negative (ahead of schedule).
';


-- Tables
alter table ropidgtfs_stop_times_actual
    add column computed_dwell_time_seconds smallint not null GENERATED ALWAYS AS (calculate_dwell_time_seconds(arrival_time, departure_time)) stored;

alter table ropidgtfs_stop_times
    add column computed_dwell_time_seconds smallint;

alter table vehiclepositions_trips
    add column provider_source_type varchar(1) not null default '0';

alter table vehiclepositions_trips_history
    add column provider_source_type varchar(1) not null default '0';

comment on column vehiclepositions_trips.provider_source_type is '
0 - unknown
1 - http
2 - tcp common
3 - tcp metro
';

update vehiclepositions_trips vt
    set provider_source_type =
        case when vp.tcp_event is not null
            then '2' -- tcp common
            when vp.tcp_event is null and vp.scheduled_timestamp is not null
                then '3' -- tcp metro
            else '1' -- http
        end
from vehiclepositions_positions vp
where vp.id = vt.last_position_id;


-- Views
create view v_vehiclepositions_stop_time_delay_prediction as
with recursive stop_times as (
    select
    	rst.trip_id,
        vt.provider_source_type,
    	vp.delay,
    	coalesce(vp.last_stop_sequence, 1) as initial_stop_sequence,
        vp.state_position,
    	rst.stop_sequence,
    	rst.stop_id,
    	rst.computed_dwell_time_seconds,
    	case when vp.last_stop_sequence is null
            then vp.delay
            else vp.delay_stop_arrival
        end as arrival_delay_seconds,
    	case when vp.last_stop_sequence is null
            then predict_delay_seconds(vp.delay, rst.computed_dwell_time_seconds, vt.provider_source_type)
            when vp.state_position = 'at_stop'
                then case when vt.provider_source_type = '1' then greatest(0, vp.delay) else vp.delay end
            else vp.delay_stop_departure
        end as departure_delay_seconds
    from ropidgtfs_stop_times rst
    inner join vehiclepositions_trips vt on vt.gtfs_trip_id = rst.trip_id
    inner join vehiclepositions_positions vp on vp.id = vt.last_position_id
        and (vp.valid_to is null or vp.valid_to >= extract(epoch from now()) * 1000)
    where
        vt.gtfs_trip_id is not null
        and rst.stop_sequence = coalesce(vp.last_stop_sequence, 1)
union all
    select
    	rst.trip_id,
        previous_row.provider_source_type,
    	previous_row.delay,
    	previous_row.initial_stop_sequence,
        previous_row.state_position,
    	rst.stop_sequence,
    	rst.stop_id,
    	rst.computed_dwell_time_seconds,
    	case when rst.stop_sequence - previous_row.initial_stop_sequence = 1 and previous_row.state_position != 'at_stop'
            then previous_row.delay
            else previous_row.departure_delay_seconds
        end as arrival_delay_seconds,
        predict_delay_seconds(
            case when rst.stop_sequence - previous_row.initial_stop_sequence = 1 and previous_row.state_position != 'at_stop' then previous_row.delay else previous_row.departure_delay_seconds end,
            rst.computed_dwell_time_seconds,
            previous_row.provider_source_type
        ) as departure_delay_seconds
    from stop_times previous_row
    inner join ropidgtfs_stop_times rst on rst.trip_id = previous_row.trip_id and rst.stop_sequence = previous_row.stop_sequence + 1
)
select trip_id, stop_sequence, stop_id, arrival_delay_seconds, departure_delay_seconds
from stop_times
order by trip_id, stop_sequence;

comment on view v_vehiclepositions_stop_time_delay_prediction is '
This view contains the predicted delay of a vehicle at each stop.
';
