package me.tagavari.airmessage.connection.comm5;

import androidx.core.util.Consumer;
import me.tagavari.airmessage.connection.encryption.EncryptionManager;
import me.tagavari.airmessage.enums.ConnectionErrorCode;

import java.io.DataOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class ProxyDirectTCPWriter extends Thread {
	//Creating the queue
	final BlockingQueue<EncryptedPacket> uploadQueue = new LinkedBlockingQueue<>();
	
	//Creating the parameter values
	private final Consumer<Integer> errorListener;
	private final EncryptionManager encryptionManager;
	private final DataOutputStream outputStream;
	
	public ProxyDirectTCPWriter(Consumer<Integer> errorListener, EncryptionManager encryptionManager, DataOutputStream outputStream) {
		this.errorListener = errorListener;
		this.encryptionManager = encryptionManager;
		this.outputStream = outputStream;
	}
	
	@Override
	public void run() {
		EncryptedPacket packet;
		
		try {
			while(!isInterrupted()) {
				try {
					packet = uploadQueue.take();
					sendDataSync(packet.getData(), packet.getEncrypt());
					
					while((packet = uploadQueue.poll()) != null) {
						sendDataSync(packet.getData(), packet.getEncrypt());
					}
					
					outputStream.flush();
				} catch(IOException | GeneralSecurityException exception) {
					exception.printStackTrace();
					
					errorListener.accept(ConnectionErrorCode.connection);
				}
			}
		} catch(InterruptedException exception) {
			exception.printStackTrace();
		}
	}
	
	void queuePacket(EncryptedPacket packet) {
		uploadQueue.add(packet);
	}
	
	/**
	 * Encrypts data if necessary, and then submits it to the stream
	 * @param data The data to send
	 * @param isEncrypted Whether to encrypt this data
	 */
	private synchronized void sendDataSync(byte[] data, boolean isEncrypted) throws IOException, GeneralSecurityException {
		if(isEncrypted) data = encryptionManager.encrypt(data);
		
		outputStream.writeInt(data.length);
		outputStream.writeBoolean(isEncrypted);
		outputStream.write(data);
	}
}