/**
 * Copyright (c) 2014-2016 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.openhab.binding.zwave.internal.converter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.State;
import org.openhab.binding.zwave.handler.ZWaveControllerHandler;
import org.openhab.binding.zwave.handler.ZWaveThingChannel;
import org.openhab.binding.zwave.internal.protocol.SerialMessage;
import org.openhab.binding.zwave.internal.protocol.ZWaveNode;
import org.openhab.binding.zwave.internal.protocol.commandclass.ZWaveClockCommandClass;
import org.openhab.binding.zwave.internal.protocol.commandclass.ZWaveCommandClass;
import org.openhab.binding.zwave.internal.protocol.event.ZWaveCommandClassValueEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * ZWaveClockConverter class. Converter for communication with the {@link ZWaveClockCommandClass}.
 *
 * @author Chris Jackson
 */
public class ZWaveClockConverter extends ZWaveCommandClassConverter {

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

    /**
     * Constructor. Creates a new instance of the {@link ZWaveClockConverter} class.
     *
     */
    public ZWaveClockConverter(ZWaveControllerHandler controller) {
        super(controller);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<SerialMessage> executeRefresh(ZWaveThingChannel channel, ZWaveNode node) {
        ZWaveClockCommandClass commandClass = (ZWaveClockCommandClass) node
                .resolveCommandClass(ZWaveCommandClass.CommandClass.CLOCK, channel.getEndpoint());
        if (commandClass == null) {
            return null;
        }

        logger.debug("NODE {}: Generating poll message for {} endpoint {}", node.getNodeId(),
                commandClass.getCommandClass().getLabel(), channel.getEndpoint());
        SerialMessage serialMessage = node.encapsulate(commandClass.getValueMessage(), commandClass,
                channel.getEndpoint());
        List<SerialMessage> response = new ArrayList<SerialMessage>(1);
        response.add(serialMessage);
        return response;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public State handleEvent(ZWaveThingChannel channel, ZWaveCommandClassValueEvent event) {
        int offsetAllowed = Integer.MAX_VALUE;
        String offsetString = channel.getArguments().get("config_offset");
        if (offsetString != null && Integer.parseInt(offsetString) != 0) {
            offsetAllowed = Integer.parseInt(offsetString);
        }

        State state = null;
        switch (channel.getDataType()) {
            case DecimalType:
                // Calculate the clock offset
                Date nodeTime = (Date) event.getValue();
                long clockOffset = Math.abs(nodeTime.getTime() - System.currentTimeMillis()) / 1000;

                // If the clock is outside the offset, then update
                if (clockOffset > offsetAllowed) {
                    logger.debug("NODE {}: Clock was {} seconds off. Time will be updated.", event.getNodeId(),
                            clockOffset);

                    ZWaveNode node = controller.getNode(event.getNodeId());
                    ZWaveClockCommandClass commandClass = (ZWaveClockCommandClass) node
                            .resolveCommandClass(ZWaveCommandClass.CommandClass.CLOCK, channel.getEndpoint());

                    SerialMessage serialMessage = node.encapsulate(commandClass.getSetMessage(new Date()), commandClass,
                            channel.getEndpoint());
                    if (serialMessage == null) {
                        logger.warn("Generating message failed for command class = {}, node = {}, endpoint = {}",
                                commandClass.getCommandClass().getLabel(), node.getNodeId(), channel.getEndpoint());
                        return null;
                    } else {
                        controller.sendData(serialMessage);
                    }
                }

                state = new DecimalType(offsetAllowed);
                break;
            default:
                logger.warn("No conversion in {} to {}", this.getClass().getSimpleName(), channel.getDataType());
                break;
        }

        return state;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<SerialMessage> receiveCommand(ZWaveThingChannel channel, ZWaveNode node, Command command) {
        ZWaveClockCommandClass commandClass = (ZWaveClockCommandClass) node
                .resolveCommandClass(ZWaveCommandClass.CommandClass.CLOCK, channel.getEndpoint());

        SerialMessage serialMessage = node.encapsulate(commandClass.getSetMessage(new Date()), commandClass,
                channel.getEndpoint());
        if (serialMessage == null) {
            logger.warn("Generating message failed for command class = {}, node = {}, endpoint = {}",
                    commandClass.getCommandClass().getLabel(), node.getNodeId(), channel.getEndpoint());
            return null;
        }

        List<SerialMessage> messages = new ArrayList<SerialMessage>();
        messages.add(serialMessage);
        return messages;
    }
}

@@ -1465,24 +1465,72 @@
 er.class);%0A%0A
+    private Date lastClockUpdate = new Date();%0A%0A
     /**%0A    
@@ -3405,16 +3405,78 @@
 tAllowed
+ && lastClockUpdate.getTime() %3C (new Date().getTime() - 30000)
 ) %7B%0A    
@@ -4503,16 +4503,17 @@
       %7D%0A
+%0A
         
@@ -4524,64 +4524,890 @@
     
-%7D%0A%0A                state = new DecimalType(offsetAllowed
+    // We keep track of the last time we set the time to avoid a pathalogical loop if the time set%0A                    // doesn't work%0A                    lastClockUpdate = new Date();%0A%0A                    // And request a read-back%0A                    serialMessage = node.encapsulate(commandClass.getValueMessage(), commandClass,%0A                            channel.getEndpoint());%0A                    if (serialMessage == null) %7B%0A                        logger.warn(%22Generating message failed for command class = %7B%7D, node = %7B%7D, endpoint = %7B%7D%22,%0A                                commandClass.getCommandClass().getLabel(), node.getNodeId(), channel.getEndpoint());%0A                        return null;%0A                    %7D else %7B%0A                        controller.sendData(serialMessage);%0A                    %7D%0A                %7D%0A%0A                state = new DecimalType(clockOffset
 );%0A 

bring clock converter inline with time_parameter (#841)