package org.yamcs.studio.core.pvmanager;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.yamcs.protobuf.Pvalue.AcquisitionStatus;
import org.yamcs.protobuf.Pvalue.ParameterValue;
import org.yamcs.utils.TimeEncoding;

/**
 *
 * LosTracker
 *
 * Keep the last PV received and trigger an action if a PV expires.
 *
 */
public class LosTracker {
    private static final Logger log = Logger.getLogger(LosTracker.class.getName());

    Map<YamcsPVReader, PvExpiration> pvs = new HashMap<>();
    private final ScheduledExecutorService scheduler =
            Executors.newScheduledThreadPool(1);

    public void updatePv(YamcsPVReader pvReader, ParameterValue pval)
    {
        if (!pvs.containsKey(pvReader))
        {
            PvExpiration pvExpiration = new PvExpiration(pvReader);
            pvs.put(pvReader, pvExpiration);
        }
        PvExpiration pvExpiration = pvs.get(pvReader);
        pvExpiration.updateLosHandle(pval);
    }
    
    public void shutdown() {
        scheduler.shutdown();
    }

    class PvExpiration
    {
        YamcsPVReader pvReader;
        ParameterValue lastKnownValue;
        ScheduledFuture<?> losHandle;

        public PvExpiration(YamcsPVReader pvReader)
        {
            this.pvReader = pvReader;
        }

        public void updateLosHandle(ParameterValue lastKnownValue)
        {
            if (this.losHandle != null)
                this.losHandle.cancel(false);
            this.lastKnownValue = lastKnownValue;

            if (!lastKnownValue.hasExpirationTime())
                return;

            Calendar now = Calendar.getInstance();
            Calendar expirationTime = TimeEncoding.toCalendar(lastKnownValue.getExpirationTime());

            long expirationDelayMs = expirationTime.getTimeInMillis() - now.getTimeInMillis();

            losHandle = scheduler.schedule(displayLos, expirationDelayMs, TimeUnit.MILLISECONDS);
        }

        private final Runnable displayLos = new Runnable() {
            @Override
            public void run() {
                log.fine("LOS for parameter " + pvReader.getId().getName());
                pvReader.processParameterValue(ParameterValue.newBuilder(lastKnownValue).setAcquisitionStatus(AcquisitionStatus.EXPIRED).build());
            }
        };
    }

}

@@ -421,16 +421,115 @@
 rValue;%0A
+import org.yamcs.studio.core.model.TimeCatalogue;%0Aimport org.yamcs.studio.core.model.TimeListener;%0A
 import o
@@ -681,16 +681,40 @@
 Tracker 
+implements TimeListener 
 %7B%0A    pr
@@ -788,24 +788,75 @@
 etName());%0A%0A
+    Calendar missionTime = Calendar.getInstance();%0A
     Map%3CYamc
@@ -1004,24 +1004,120 @@
 adPool(1);%0A%0A
+    public LosTracker()%0A    %7B%0A        TimeCatalogue.getInstance().addTimeListener(this);%0A    %7D%0A%0A
     public v
@@ -1458,20 +1458,16 @@
 ;%0A    %7D%0A
-    
 %0A    pub
@@ -1844,32 +1844,33 @@
 alue)%0A        %7B%0A
+%0A
             if (
@@ -2041,16 +2041,39 @@
 onTime()
+ %7C%7C missionTime == null
 )%0A      
@@ -2095,32 +2095,34 @@
 n;%0A%0A            
+//
 Calendar now = C
@@ -2316,19 +2316,27 @@
 lis() - 
-now
+missionTime
 .getTime
@@ -2828,10 +2828,271 @@
 %0A    %7D%0A%0A
+    @Override%0A    public void processTime(long newMissionTime) %7B%0A        if (newMissionTime == TimeEncoding.INVALID_INSTANT %7C%7C newMissionTime == 0) %7B%0A            missionTime = null;%0A        %7D%0A        missionTime = TimeEncoding.toCalendar(newMissionTime);%0A    %7D%0A
 %7D%0A

check expiration time against mission time (instead of current time)