/*
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.java.otr4j.io;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;

import javax.crypto.interfaces.DHPublicKey;

import net.java.otr4j.io.messages.SignatureM;
import net.java.otr4j.io.messages.MysteriousT;
import net.java.otr4j.io.messages.SignatureX;

import org.bouncycastle.util.BigIntegers;

/**
 * @author George Politis
 */
public class OtrOutputStream extends FilterOutputStream implements
		SerializationConstants
{
	public OtrOutputStream(OutputStream out) {
		super(out);
	}

	private void writeNumber(int value, int length) throws IOException {
		byte[] b = new byte[length];
		for (int i = 0; i < length; i++) {
			int offset = (b.length - 1 - i) * 8;
			b[i] = (byte) ((value >>> offset) & 0xFF);
		}
		write(b);
	}

	public void writeBigInt(BigInteger bi) throws IOException {
		byte[] b = BigIntegers.asUnsignedByteArray(bi);
		writeData(b);
	}

	public void writeByte(int b) throws IOException {
		writeNumber(b, TYPE_LEN_BYTE);
	}

	public void writeData(byte[] b) throws IOException {
		int len = (b == null || b.length < 0) ? 0 : b.length;
		writeNumber(len, DATA_LEN);
		if (len > 0)
			write(b);
	}

	public void writeInt(int i) throws IOException {
		writeNumber(i, TYPE_LEN_INT);

	}

	public void writeShort(int s) throws IOException {
		writeNumber(s, TYPE_LEN_SHORT);

	}

	public void writeMac(byte[] mac) throws IOException {
		if (mac == null || mac.length != TYPE_LEN_MAC)
			throw new IllegalArgumentException();

		write(mac);
	}

	public void writeCtr(byte[] ctr) throws IOException {
		if (ctr == null || ctr.length < 1)
			return;

		int i = 0;
		while (i < TYPE_LEN_CTR && i < ctr.length) {
			write(ctr[i]);
			i++;
		}
	}

	public void writeDHPublicKey(DHPublicKey dhPublicKey) throws IOException {
		byte[] b = BigIntegers.asUnsignedByteArray(dhPublicKey.getY());
		writeData(b);
	}

	public void writePublicKey(PublicKey pubKey) throws IOException {
		if (!(pubKey instanceof DSAPublicKey))
			throw new UnsupportedOperationException(
					"Key types other than DSA are not supported at the moment.");

		DSAPublicKey dsaKey = (DSAPublicKey) pubKey;

		writeShort(0);

		DSAParams dsaParams = dsaKey.getParams();
		writeBigInt(dsaParams.getP());
		writeBigInt(dsaParams.getQ());
		writeBigInt(dsaParams.getG());
		writeBigInt(dsaKey.getY());

	}

	public void writeTlvData(byte[] b) throws IOException {
		int len = (b == null || b.length < 0) ? 0 : b.length;
		writeNumber(len, TLV_LEN);
		if (len > 0)
			write(b);
	}

	public void writeSignature(byte[] signature, PublicKey pubKey)
			throws IOException
	{
		if (!pubKey.getAlgorithm().equals("DSA"))
			throw new UnsupportedOperationException();
		out.write(signature);
	}

	public void writeMysteriousX(SignatureX x) throws IOException {
		writePublicKey(x.longTermPublicKey);
		writeInt(x.dhKeyID);
		writeSignature(x.signature, x.longTermPublicKey);
	}

	public void writeMysteriousX(SignatureM m) throws IOException {
		writeBigInt(m.localPubKey.getY());
		writeBigInt(m.remotePubKey.getY());
		writePublicKey(m.localLongTermPubKey);
		writeInt(m.keyPairID);
	}

	public void writeMysteriousT(MysteriousT t) throws IOException {
		writeShort(t.protocolVersion);
		writeByte(t.messageType);
		if (t.protocolVersion == 3) {
			writeInt(t.senderInstanceTag);
			writeInt(t.receiverInstanceTag);
		}
		writeByte(t.flags);

		writeInt(t.senderKeyID);
		writeInt(t.recipientKeyID);
		writeDHPublicKey(t.nextDH);
		writeCtr(t.ctr);
		writeData(t.encryptedMessage);

	}
}

@@ -1886,32 +1886,16 @@
  == null
- %7C%7C b.length %3C 0
 ) ? 0 : 
@@ -3272,24 +3272,8 @@
 null
- %7C%7C b.length %3C 0
 ) ? 

remove two conditions that can never return true