/*******************************************************************************
 * Copyright (c) 2009, 2014 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution. 
 *
 * The Eclipse Public License is available at 
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    Dave Locke - initial API and implementation and/or initial documentation
 *    Ian Craggs - MQTT 3.1.1 support
 */
package org.eclipse.paho.client.mqttv3.internal.wire;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

/**
 * An on-the-wire representation of an MQTT CONNECT message.
 */
public class MqttConnect extends MqttWireMessage {

	public static final String KEY = "Con";

	private String clientId;
	private boolean cleanSession;
	private MqttMessage willMessage;
	private String userName;
	private char[] password;
	private int keepAliveInterval;
	private String willDestination;
	private int mqttVersion;

	/**
	 * Constructor for an on the wire MQTT connect message
	 * 
	 * @param info
	 *            The info byte
	 * @param data
	 *            the data byte array
	 * @throws IOException
	 *             thrown if an exception occurs when reading the input streams
	 * @throws MqttException
	 *             thrown if an exception occurs when decoding UTF-8
	 */
	public MqttConnect(byte info, byte[] data) throws IOException, MqttException {
		super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
		DataInputStream dis = new DataInputStream(bais);

		String protocol_name = decodeUTF8(dis);
		int protocol_version = dis.readByte();
		byte connect_flags = dis.readByte();
		keepAliveInterval = dis.readUnsignedShort();
		clientId = decodeUTF8(dis);
		dis.close();
	}

	public MqttConnect(String clientId, int mqttVersion, boolean cleanSession, int keepAliveInterval, String userName,
			char[] password, MqttMessage willMessage, String willDestination) {
		super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
		this.clientId = clientId;
		this.cleanSession = cleanSession;
		this.keepAliveInterval = keepAliveInterval;
		this.userName = userName;
		if (password != null) {
			this.password = password.clone();
		}
		this.willMessage = willMessage;
		this.willDestination = willDestination;
		this.mqttVersion = mqttVersion;
	}

	public String toString() {
		String rc = super.toString();
		rc += " clientId " + clientId + " keepAliveInterval " + keepAliveInterval;
		return rc;
	}

	protected byte getMessageInfo() {
		return (byte) 0;
	}

	public boolean isCleanSession() {
		return cleanSession;
	}

	protected byte[] getVariableHeader() throws MqttException {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			DataOutputStream dos = new DataOutputStream(baos);

			if (mqttVersion == 3) {
				encodeUTF8(dos, "MQIsdp");
			} else if (mqttVersion == 4) {
				encodeUTF8(dos, "MQTT");
			}
			dos.write(mqttVersion);

			byte connectFlags = 0;

			if (cleanSession) {
				connectFlags |= 0x02;
			}

			if (willMessage != null) {
				connectFlags |= 0x04;
				connectFlags |= (willMessage.getQos() << 3);
				if (willMessage.isRetained()) {
					connectFlags |= 0x20;
				}
			}

			if (userName != null) {
				connectFlags |= 0x80;
				if (password != null) {
					connectFlags |= 0x40;
				}
			}
			dos.write(connectFlags);
			dos.writeShort(keepAliveInterval);
			dos.flush();
			return baos.toByteArray();
		} catch (IOException ioe) {
			throw new MqttException(ioe);
		}
	}

	public byte[] getPayload() throws MqttException {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			DataOutputStream dos = new DataOutputStream(baos);
			encodeUTF8(dos, clientId);

			if (willMessage != null) {
				encodeUTF8(dos, willDestination);
				dos.writeShort(willMessage.getPayload().length);
				dos.write(willMessage.getPayload());
			}

			if (userName != null) {
				encodeUTF8(dos, userName);
				if (password != null) {
					encodeUTF8(dos, new String(password));
				}
			}
			dos.flush();
			return baos.toByteArray();
		} catch (IOException ex) {
			throw new MqttException(ex);
		}
	}

	/**
	 * Returns whether or not this message needs to include a message ID.
	 */
	public boolean isMessageIdRequired() {
		return false;
	}

	public String getKey() {
		return KEY;
	}
}
