/**
 * Copyright (c) 2014-2017 by the respective copyright holders.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipse.smarthome.binding.lifx.internal;

import java.nio.ByteBuffer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import org.eclipse.smarthome.binding.lifx.LifxBindingConstants;
import org.eclipse.smarthome.binding.lifx.handler.LifxLightHandler.CurrentLightState;
import org.eclipse.smarthome.binding.lifx.internal.fields.MACAddress;
import org.eclipse.smarthome.binding.lifx.internal.listener.LifxResponsePacketListener;
import org.eclipse.smarthome.binding.lifx.internal.protocol.EchoRequestResponse;
import org.eclipse.smarthome.binding.lifx.internal.protocol.GetEchoRequest;
import org.eclipse.smarthome.binding.lifx.internal.protocol.GetServiceRequest;
import org.eclipse.smarthome.binding.lifx.internal.protocol.Packet;
import org.eclipse.smarthome.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link LifxLightOnlineStateUpdater} sets the state of a light offline when it no longer responds to echo packets.
 *
 * @author Wouter Born - Extracted class from LifxLightHandler
 */
public class LifxLightOnlineStateUpdater implements LifxResponsePacketListener {

    private static final int ECHO_POLLING_INTERVAL = 15;
    private static final int MAXIMUM_POLLING_RETRIES = 4;

    private final Logger logger = LoggerFactory.getLogger(LifxLightOnlineStateUpdater.class);

    private final String macAsHex;
    private final CurrentLightState currentLightState;
    private final LifxLightCommunicationHandler communicationHandler;

    private final ReentrantLock lock = new ReentrantLock();

    private final ScheduledExecutorService scheduler = ThreadPoolManager
            .getScheduledPool(LifxBindingConstants.THREADPOOL_NAME);

    private ScheduledFuture<?> echoJob;
    private int unansweredEchoPackets;

    public LifxLightOnlineStateUpdater(MACAddress macAddress, CurrentLightState currentLightState,
            LifxLightCommunicationHandler communicationHandler) {
        this.macAsHex = macAddress.getHex();
        this.currentLightState = currentLightState;
        this.communicationHandler = communicationHandler;
    }

    private Runnable echoRunnable = new Runnable() {

        @Override
        public void run() {
            try {
                lock.lock();
                logger.trace("{} : Polling", macAsHex);
                if (currentLightState.isOnline()) {
                    if (unansweredEchoPackets < MAXIMUM_POLLING_RETRIES) {
                        ByteBuffer payload = ByteBuffer.allocate(Long.SIZE / 8);
                        payload.putLong(System.currentTimeMillis());

                        GetEchoRequest request = new GetEchoRequest();
                        request.setResponseRequired(true);
                        request.setPayload(payload);

                        communicationHandler.sendPacket(request);
                        unansweredEchoPackets++;
                    } else {
                        currentLightState.setOfflineByCommunicationError();
                        unansweredEchoPackets = 0;
                    }
                } else {
                    // are we not configured? let's broadcast instead
                    logger.trace("{} : The light is not online, let's broadcast instead", macAsHex);
                    GetServiceRequest packet = new GetServiceRequest();
                    communicationHandler.broadcastPacket(packet);
                }
            } catch (Exception e) {
                logger.error("Error occurred while polling online state", e);
            } finally {
                lock.unlock();
            }
        }
    };

    public void start() {
        try {
            lock.lock();
            communicationHandler.addResponsePacketListener(this);
            if (echoJob == null || echoJob.isCancelled()) {
                echoJob = scheduler.scheduleWithFixedDelay(echoRunnable, 0, ECHO_POLLING_INTERVAL, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            logger.error("Error occurred while starting online state poller", e);
        } finally {
            lock.unlock();
        }
    }

    public void stop() {
        try {
            lock.lock();
            communicationHandler.removeResponsePacketListener(this);
            if (echoJob != null && !echoJob.isCancelled()) {
                echoJob.cancel(true);
                echoJob = null;
            }
        } catch (Exception e) {
            logger.error("Error occurred while stopping online state poller", e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void handleResponsePacket(Packet packet) {
        if (packet instanceof EchoRequestResponse) {
            unansweredEchoPackets = 0;
        }
    }

}

@@ -389,24 +389,83 @@
 ByteBuffer;%0A
+import java.time.Duration;%0Aimport java.time.LocalDateTime;%0A
 import java.
@@ -951,89 +951,8 @@
 er;%0A
-import org.eclipse.smarthome.binding.lifx.internal.protocol.EchoRequestResponse;%0A
 impo
@@ -1677,17 +1677,17 @@
 TRIES = 
-4
+3
 ;%0A%0A    p
@@ -2181,16 +2181,72 @@
 choJob;%0A
+    private LocalDateTime lastSeen = LocalDateTime.MIN;%0A
     priv
@@ -2855,24 +2855,140 @@
 Online()) %7B%0A
+                    if (Duration.between(lastSeen, LocalDateTime.now()).getSeconds() %3E ECHO_POLLING_INTERVAL) %7B%0A    
             
@@ -3046,24 +3046,28 @@
 _RETRIES) %7B%0A
+    
             
@@ -3159,16 +3159,20 @@
         
+    
 payload.
@@ -3233,16 +3233,20 @@
         
+  
+  
 GetEchoR
@@ -3276,32 +3276,36 @@
 tEchoRequest();%0A
+    
                 
@@ -3363,32 +3363,36 @@
                 
+    
 request.setPaylo
@@ -3421,32 +3421,36 @@
                 
+ 
+   
 communicationHan
@@ -3471,24 +3471,28 @@
 t(request);%0A
+    
             
@@ -3540,32 +3540,36 @@
                 
+    
 %7D else %7B%0A       
@@ -3585,16 +3585,20 @@
         
+ 
+   
 currentL
@@ -3657,32 +3657,36 @@
                 
+    
 unansweredEchoPa
@@ -3688,32 +3688,58 @@
 choPackets = 0;%0A
+                        %7D%0A
                 
@@ -5334,57 +5334,40 @@
     
-if (packet instanceof EchoRequestResponse) %7B%0A    
+lastSeen = LocalDateTime.now();%0A
     
@@ -5397,26 +5397,16 @@
 ts = 0;%0A
-        %7D%0A
     %7D%0A%0A%7D

Improve online/offline detection