/**
 * Copyright (c) 2010-2016, openHAB.org and others.
 *
 * 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.neohub.internal;

import static java.nio.charset.StandardCharsets.US_ASCII;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * NeoHubConnector handles the ASCII based TCP communication between openhab and
 * neohub.
 *
 * @author Sebastian Prehn
 * @since 1.5.0
 */
public class NeoHubConnector {
    private static final Logger logger = LoggerFactory.getLogger(NeoHubConnector.class);

    /**
     * Name of host or IP to connect to.
     */
    private final String hostname;

    /**
     * The port to connect to.
     */
    private final int port;

    /**
     * Maximum time in ms to wait for neohub's response. Defaults to 5000ms.
     */
    private final int timeout = 5000;

    public NeoHubConnector(final String hostname, final int port) {
        this.hostname = hostname;
        this.port = port;
    }

    /**
     * Sends the message over the network and provides the response to the
     * response handler.
     * 
     * @param msg
     *            message to neohub
     * @param handler
     *            handler to process the response, may be <code>null</code>
     * @return result of handler or <code>null</code> if network problem
     *         occurred
     */
    public <T> T sendMessage(final String msg, final ResponseHandler<T> handler) {
        final StringBuilder response = new StringBuilder();
        final Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress(hostname, port), timeout);
            final InputStreamReader in = new InputStreamReader(socket.getInputStream(), US_ASCII);
            final OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream(), US_ASCII);
            logger.debug(">> {}", msg);
            out.write(msg);
            out.write(0); // NUL terminate the command string
            out.flush();

            int l;
            while ((l = in.read()) > 0) {// NUL termination & end of stream (-1)
                response.append((char) l);
            }
        } catch (final IOException e) {
            logger.error("Failed to connect to neohub [host '{}' port '{}' timeout '{}']",
                    new Object[] { hostname, port, timeout });
            logger.debug("Failed to connect to neohub.", e);
            return null;
        } finally {
            IOUtils.closeQuietly(socket);
        }
        final String responseStr = response.toString();
        logger.debug("<< {}", responseStr);

        if (handler != null) {
            return handler.onResponse(responseStr);
        } else {
            return null;
        }
    }
}